A podcast about development and design.
We do our best to be code-agnostic but we talk a lot about Rails, JavaScript, React, React Native, design, business and startups.
php/*
A podcast about development and design.
We do our best to be code-agnostic but we talk a lot about Rails, JavaScript, React, React Native, design, business and startups.
Copyright: © ¯_(ツ)_/¯
Welcome to Iteration: A weekly podcast about programming, development, and design through the lens of amazing books, chapter-by-chapter
Ask yourself:
there are no silver bullets
Picks:
https://www.notion.so/ - How meta - we use Notion to manage this podcast.
A weekly podcast about programming, development, and design through the lens of amazing books, chapter-by-chapter.
John: How / Why do they grow? Needs change:
John: All the different use cases for a user

John:
"When you need to implement password recovery, and do not have a clear, single place to put the logic, it will still find its way into your code. It will spread itself across existing classes, usually making those classes harder to read and use.”
Example problem: Want to send a welcome email when a user is created via a public form but not when an admin creates a user via a backend interface
Core models should only have the absolute minimum to exist:
Core models should NOT have these things: (these things belong in multiple, interaction-specific form models)
virtual attributes that don't map 1:1 with db
callbacks to fire for a particular screen or use case (i.e. form signup)
we want the perks of AR models with AR. solution: inheritance
class User::AsSignUp < User
validates :password, presence: true, confirmation: true
after_create :send_welcome_email
private
def send_welcome_email; end
end
John: Note the "AsSignup" pattern - "AsFacebookAuth"
def self.somethingclass Invoice < ActiveRecord::Base
has_many :items
end
class Item < ActiveRecord::Base
belongs_to:invoice
end
Why not just:
class Invoice::Item < ActiveRecord::Base
belongs_to:invoice
end
and move the file to: app/models/invoice/item.rb
The recommendations are a lot like BEM - A front-end development methodology - learn more at: http://getbem.com/
Abstracts (Sizing, Boarders, Spacing)Base (Grid, Colors, images, Typography)Components (Buttons, Cards, Alerts)Page Specific CSS
Picks:
Growing Rails Applications in Practice: Part 1/3: New Rules For Rails
Welcome to Iteration: A weekly podcast about programming, development, and design through the lens of amazing books, chapter-by-chapter
An Inconvenient Secret: Large applications are large
This book is about scaling your codebase. The amount of pain you feel should be logarithmic, not exponential.
This is not about revolutionary design patterns or magic gems that make all your problems go away. Instead, we will show you how to use discipline, consistency, and organization to make your application grow more gently.
Beautiful Controllers
Should functionality go into the model or the controller?!
"Less business logic in our controllers, the better"
The authors argue for a standard controller design for every single user interaction. that is to say, it should only handle HTTP requests
EVERYTHING is CRUD
example of a stripped-down controller where Note controller never talks to the model directly.
"Every controller action reads or changes a single model. even if an update involves multiple models, the job of finding and changing the involved records should be pushed to an orchestrating model"
controllers should contain the minimum amount of glue to translate between the request, your model, and the response
Relearning Active Record
Data integrity with callbacks -> later in the book, we'll talk about how to not use so many callbacks
bad:
class Invite < ActiveRecord::Base
def accept! (user)
accepted = true
Membership.create!(user: user)
end
end
invite.update(accepted: true) # => circumventing the accept! method
better:
after_save :create_membership_on_accept
private
def create_membership_on_accept
if accepted && accepted_changed?
Membership.create!(user: user)
end
end
User interactions without a database
Not all user interactions need an active record model.
Example: sign in form that uses sessions. when the form is submitted, you don't end up inserting a row in a table in your db
start taking your controllers from hell -> putting that logic in your model (models from hell?)
you know things are a pain when you have to use form_tag and can't use form_for
one thing to note is that the example sends notifications (i.e. SMS, email) in their model and not their controller
Welcome to Iteration - A weekly podcast about programming, development, and design through the lens of amazing books, chapter-by-chapter.
In this more casual episode, we recap some of our favorite tips from the Pragmatic Programmer in the context of our recent projects and lessons. We talk though caring about your craft, not leaving any broken windows and more.
Picks:
John - http://www.hemingwayapp.com/
JP - better touch tool
John: Welcome to Iteration: A weekly podcast about programming, development, and
design through the lens of amazing books, chapter-by-chapter.
JP: This is part 2 of Chapter 8 - THE FINAL CHAPTER!
Identify and test significant program states. Just testing lines of code isn't enough
JP: Even if you test every single line of code - that's not what matters. What matters is how many different permutations of your app's state you have covered. drops mic
John: Coverage tools are nice - but they only give you a window into the state passed into it. If you can extract state out into separate objects or methods to be able to test them decoupled from time.
Once a human tester finds a bug, it should be the last time a human tester finds that bug. Automatic tests should check for it from then on.
JP: We don't have time to find a bug more than once... "We have to spend our time writing new code - and new bugs"
John: Step one of debugging - write a test that's failing. I broke this pattern this weekend. :(
Write docs as you would write code: honor the DRY principle, use metadata, MVC, auto-generation and so on
JP: don't dread writing docs. it's part of your application. I think this tip is phrased in such a way to appeal to engineers. think of documentation writing as writing code, not writing literature
John: I like this in theory - I'm having trouble getting tooling in place for this that makes sense. I really want to dynamically generate, external API docs, internal docs and user guides totally automatically. Super struggling to get anything moving in rails.
Documentation created separately from code is less likely to be correct and up to date
JP: sometimes you need documentation. obviously, name your variables well, but sometimes explain what a function does or why it does it. at OL we'll sometimes write include the GitHub issue number for a weird quirk or to explain why a test is testing for certain functionality. treat docs like code. part of the code, not separate from it
John: I feel like this is a bit outdated. If written well modern languages really can be self-documenting. Basecamp really doesn't "do" code comments or internal docs - Per DHH - If there is a comment explaining a method or model I break it up further until it doesn't need explaining. I worship in the church of Henninger-Hansen.
Come to understand your users' expectations, then deliver just that little bit more.
JP: "Never lose sight of the business problems your application is intended to solve".
John: One of my favorite parts of the job is delivering features users get super jazzed about.
Craftspeople of an earlier age were proud to sign their work. You should be, too
"Anonymity, especially on large projects, can provide a breeding ground for sloppiness, mistakes, sloth, and bad code. [...] We want to see a pride of ownership. Your signature should come to be recognized as an indicator of quality. A really professional job. Written by a real programmer. A Pragmatic Programmer"
JP: lol, git blame
John: Take pride in what you do.
Picks
JP: https://github.com/sindresorhus/refined-github
John: LG 22MD4KA-B UltraFine 4K Display 21.5" UHD 4096x2304 4xUSB-C
Next week: Pragmatic Programmer in Practice - Book recap in the context of our real-life development work.
John: Welcome to Iteration: A weekly podcast about programming, development, and
design through the lens of amazing books, chapter-by-chapter.
JP: This is part 1 of Chapter 8
John: This chapter is all about "Pragmatic Projects" - Teams, Automation, Testing, Documentation Code quality and more.
Don't separate designers from coders, testers from data modelers. Build teams the way you would build code.
JP: It's a mistake to think that the activities of a project - analysis, design, coding, and testing - can happen in isolation. i.e. Offers V2 at OL. Leaders of a team: needs at least 1 technical and 1 administrative personnel. Always think of the business goals
John: It's nice to have lots of full stack devs - They can focus more on a "Module" than a specific tech or "side" of the project.
At the dawn of the age of automobiles, the instructions for starting a Model-T Ford were more than two pages long. With modern cars, you just turn the key—the starting procedure is automatic and foolproof.
John: We are developers! Why would we do ANY tedious work? Example: Github's API pulls in PR's and notes.
A shell script or batch file will execute the same instructions, in the same order, time after time
JP: "We may have to build the starter and fuel injector from scratch, but once it's done, we can just turn the key from then on" i.e. deploys
Let the computer do the repetitious, the mundane—it will do a better job of it than we would.
Tests that run with every build are much more effective than test plans that sit on the shelf.
JP: In the Smalltalk world, they say, "Code a little, test a little" -> Get those small wins and make incremental changes
John: Write tests to help guide design.
'Nuff said
JP: Keeping your feature branch green!
John: ALL the tests - unit, integration, performance, staging, usability, QA
Introduce bugs on purpose in a separate copy of the source to verify that testing will catch them.
JP: After you have written a test to find a particular bug, cause the bug on purpose to make sure the test complains
John: Code coverage analysis tools are very helpful
Picks
John: Welcome to Iteration: A weekly podcast about programming, development, and
design through the lens of amazing books, chapter-by-chapter.
JP: Welcome to PART 2 of Chapter 7: "Before The Project"
You've been building experience all your life. Don't ignore
nagging doubts.
JP: Follow your instincts when starting a project - but don't
procrastinate - you can do this by prototyping. Proof of concepts are nice
when you tackle the "hard" thing first. Prototype to discover
John: Prototype, prototype!
"as the prototype progresses you may have one of those moments of revelation when you suddenly realize that some basic premise was wrong."
Don't fall into the specification spiral - at some point you need to start
coding.
JP: "Program specification is the process of taking a requirement and reducing
it down to the point where a programmer's skill can take over." -
John:
"natural language is really not up to the job."
more than 1 bullet point alert
Don't blindly adopt any technique without putting it into the context of your
development practices and capabilities
This shows how dated the book is. examples of formal methods: CASE tools,
waterfall development, "the spiral model", to today's UML diagrams.
If this were written today, we'd talk about lean methodology, agile development,
SCRUM
You should work constantly to refine and improve your process
Beware of vendor hype, industry dogma, and the aura of the price tag. Judge
tools on their merits.
John:
Try not to think about how much a tool cost when you look at its output.
JP:
If you're an Apple fan boy/girl, you might be interested in what Steve Jobs had
to say about Object-Oriented Programming. This is an excerpt from a 1994 Rolling
Stone interview where Steve (not a programmer) explains OOP in simple terms.
Jeff Goodell: Would you explain, in simple terms, exactly what object-oriented
software is?
Steve Jobs: Objects are like people. They’re living, breathing things that have
knowledge inside them about how to do things and have memory inside them so they
can remember things. And rather than interacting with them at a very low level,
you interact with them at a very high level of abstraction, like we’re doing
right here.
Here’s an example: If I’m your laundry object, you can give me your dirty
clothes and send me a message that says, “Can you get my clothes laundered,
please.” I happen to know where the best laundry place in San Francisco is. And
I speak English, and I have dollars in my pockets. So I go out and hail a
taxicab and tell the driver to take me to this place in San Francisco. I go get
your clothes laundered, I jump back in the cab, I get back here. I give you your
clean clothes and say, “Here are your clean clothes.”
You have no idea how I did that. You have no knowledge of the laundry place.
Maybe you speak French, and you can’t even hail a taxi. You can’t pay for one,
you don’t have dollars in your pocket. Yet, I knew how to do all of that. And
you didn’t have to know any of it. All that complexity was hidden inside of me,
and we were able to interact at a very high level of abstraction. That’s what
objects are. They encapsulate complexity, and the interfaces to that complexity
are high level.
John: Time Timer - https://www.timetimer.com/ - Pomodoro - Physical Device
John: Welcome to Iteration: A weekly podcast about programming, development, and
design through the lens of amazing books, chapter-by-chapter.
JP: Welcome to PART 1 of Chapter 7: "Before The Project."
Starting a project can be a daunting task. There are a lot of unknowns and you
might not know when you should start. We're going to talk about some of the
things that will guide you when starting a project from scratch. Our focus will
be gathering and understanding requirements
For me, this is great because though I work at OL, I might be taking on a
freelance client soon!
John:
"Perfection is achieved, not when there is nothing left to add, but when there is nothing left to take away...."
Requirements rarely lie on the surface. They're buried deep beneath the layers
of assumptions, misconceptions, and politics
JP: You can't just "gather" requirements as if they exist. You must do RESEARCH.
Remember that you're solving business problems
It's the best way to gain insight into how the system will really be used
JP: OBSERVE users for a week - this is much different than "thinking" like a
user. I think that if you do the latter, you end up with preconceived notions
based on your own assumptions.
John:
"It's important to discover the underlying reason why users do a particular thing, rather than just the way they currently do it."
John:
"...the simplest statement that accurately reflects the business need is best."
Invest in the abstraction, not the implementation. Abstractions can survive
the barrage of changes from different implementations and new technologies
Requirements are not architecture. Requirements are not design, nor are they
the user interface. Requirements are need
JP: Don't sweat implementation details. Requirements should be abstract
Create and maintain a single source of all the specific terms and vocabulary
for a project
JP: Create a literal glossary so that you and stakeholders can have a
UBIQUITOUS LANGUAGE #domaindrivendesign - i.e. "client" and "customer"
might be the same - they might be different.
Also - you want this to live in a document that's shared. Don't know how serious
that is but this is what the author's recommend for serious projects
John:
Gordius, the King of Phrygia, once tied a knot that no one could untie. It was said that he who solved the riddle of the Gordian Knot would rule all of Asia. So along comes Alexander the Great, who chops the knot to bits with his sword. Just a little different interpretation of the requirements, that's all... and he did end up ruling most of Asia.
When faced with an impossible problem, identify the real constraints. Ask
yourself: "Does it have to be done this way? Does it have to be done at all?"
JP: Find the constraints of the box so that you can solve the problem at hand.
You have to think of every possible avenue so that you don't dismiss potential
solutions. I really like this:
Consider the Trojan horse - a novel solution to an intractable problem. How do
you get troops into a walled city without being discovered? You can be that
"through the front door" was initially dismissed as suicide.
Too many quotes alert
Many times a reinterpretation of the requirements can make a whole set of
problems go away [...]. All you need are the real constraints, the misleading
constraints, and the wisdom to know the difference.
JP: Swift - The Big Nerd Ranch Guide. Plenty of reasons why I want to learn
Swift.
John: Welcome to Iteration: A weekly podcast about programming, development, and design through the lens of amazing books, chapter-by-chapter.
JP: This is part 2 of chapter 6, "While you are coding". Last week we talked about pragmatic practices while you are coding.
Just as you might weed and rearrange a garden, rewrite, rework, and re-architect code when it needs it. Fix the root of the problem
Time pressure is often used as an excuse for not refactoring. But his excuse just doesn't hold up: fail to refactor now, and there'll be far greater time investment to fix the problem down the road - when there are more dependencies to reckon with. Will there be more time available then? Not in our experience
Advice:
Keep track of the things that need to be refactored. If you cna't refactor something immediately, make sure that it gets placed on the schedule. Make sure that users of the affected code know that it is scheduled to be refactored and how it might affect them.
Martin Fowler's tips for refactoring
1.) don't try to refactor and add functionality at the same time
2.) make sure you have good tests in place before you begin refactoring
3.) take short, deliberate steps
What's your tolerance for pain?
Start thinking about testing before you write a line of code
John: If your tests are hard to write - the design of your system is probably inelegant. It's a "design smell" if you are thinking - how the hell am I going to test this?
Test ruthlessly. Don't make your users find bugs for you.
HOT QUOTE ALERT
Testing is more cultural than technical: we can instill this testing culture in a project regardless of the language being used.
John: Related: Re-create bugs in tests when debugging. every. time.
Wizards can generate reams of code. Make sure you understand all of it before you incorporate it into your project
We are not against wizards [...] But if you do use a wizard, and you don't understand all the code that it produces, you won't be in control of your own application. You won't be able to maintain it, and you'll be struggling when it comes time to debug
PICKS
JP: https://www.howtographql.com/
John: Jesus Castello - Ruby Guides on YouTube
John: Welcome to Iteration: A weekly podcast about programming, development, and design through the lens of amazing books, chapter-by-chapter.
JP: Chapter 6 "While you are coding" Summary / Introduction - In this section, we will be discussing the things a programmer thinks about during the process of coding.
The section kicks off by talking about how coding is not a mechanical process. Adjustments must be made while we code. It's largely driving a car. Our brain goes into auto-pilot - however, an attentive driver is always assessing the current situation. Is someone about to cross the street? Etc.
Rely only on reliable things. Beware of accidental complexity, and don't confuse a happy coincidence with a purposeful plan
JP: really funny metaphor about a solider coming to a false conclusion in a minefield. "As developers, we also work in minefields."
You can't know why something is broken if you didn't know why it worked in the first place
John: It's easy to assume that X causes Y, but as we've said - don't assume it, prove it.
John: Be deliberate.
Get a feel for how long things are likely to take before you write code
JP: Basically, big O stuff: constant, logarithmic, linear, exponential. Use a greedy approach when you can. Try to think about how you can do something in a single pass. But always remember the context. Maybe your data isn't so large that an exponential algorithm is just fine for the sake of readability.
"Pragmatic programmers try to cover both the theoretical and practical bases. After all this estimating, the only timing that counts is the speed of your code, running in the prod env. with real data."
John: If I have a super slow method or view it’s usually because I need a new object or attribute.
Mathematical analysis of algorithms doesn't tell you everything. Try timing your code in its target environment
JP: "Be wary of premature optimization. It's always a good idea to make sure an alg really is a bottleneck before investing your precious time trying to improve it"
John: In general: Tests pass - I ship. I'd rather just throw more Dyno's at my methods than too much time optimizing.
PICKS
JP: browserstack.com
John: Postman - specifcally SET UP ENVIROMENTS
John: Greedy Algorithms