The video above was recorded as a keynote at RailsConf 2017. It was also presented at the inaugural DeconstructConf, which means the talk’s design benefited richly from my fear of disappointing the masterful Gary Bernhardt.
The premise
Programmers are really good at talking about what programs are. Most people will invest a decade just to get a handle on the massive compendium of jargon and metaphors we have created for describing the structure and behavior of finished programs.
But you know what programmers aren’t so practiced at articulating? How they program.
Specifically, we don’t have very evolved ways to convey clearly the details of the countless actions, feelings, and thoughts that go into writing software. We’re simply not used to talking about how we code (short of pointing to a prescribed principle or process that we do our best to imitate). My favorite Computer Science professor liked to say, “be wary; any major that has ‘Science’ in its name, isn’t”.
Surely, the fact we struggle to ask and answer “how” questions about programming has tremendous implications affecting how we learn programming, the nature of our work, the colleagues we keep, and even impacts broad industry trends. It seems obvious that we should make an effort to get better at understanding, improving, and sharing our various programming workflows. As a humble beginning, this talk lays out an approach for:
- Identifying your unique disposition as a programmer
- Introspecting how you act, feel, and think in a variety of contexts
- Using that introspection to capitalize on or mitigate those actions, feelings, or thoughts
- Sharing your personally-tailored workflow with others, so they might find aspects that would be useful in their own practice
The Searls-Briggs® Type Indicator™
A conceit of the talk is that I’m filling out a goofy little survey that establishes four buckets of, for want of a better phrase, “programmer personality types”. The quiz is a bit of a joke and not scientific at all, but despite this was very popular (things that could also be said of its namesake).
In the first weekend after I performed the talk, the quiz had over 4000 respondents, so clearly there is an untapped demand for this sort of inquiry. Here’s a breakdown of your predilections, in aggregate:
[Editor's note: This survey is now closed. It generated more than 7,000 answers. See the results here.]
Trait #1 - Fearless vs. sensitive
As you can see, there’s a healthy distribution between the two. In the talk, I identify as sensitive, meaning emotions play a really strong role in my work (both helpfully and not), which leads me to more introspection and empathy, but at the cost of lacking much courage under fire—much less excitement amid volatile circumstances.
I’m a bit envious of the majority of respondents who identified with more fearless markers, especially the 67% who prefer to hear all requirements up-front without worry of feeling overwhelmed.
Trait #2 - Invntive vs. aesthetic
This bucket is a rough analogue for where folks land on the spectrum between order and chaos. Inventive types are the ones who eagerly try out the latest and greatest languages & frameworks, whereas aesthetic-leaners (like me) tend to roll their eyes at how each subsequent cohort of new programmers insists on reinventing MVC every six months.
In the talk, the aesthetic archetype is summarized as valuing consistency and an idyllic vision of how code ought to be structured, even if those ideals are—to an extent—subject to the fashions of the moment. Inventive types, meanwhile, will gladly trade purist ideals for the opportunity to cover a wide breadth of tools & frameworks if it yields more novel interesting experiences while writing software.
Trait #3 - Naive vs. leery
Being leery myself, I was not surprised to see the majority of respondents rated “Naive”. Both are negatively-connoted words, but don’t take offense—this bucket measures bad habits at both extremes.
Naive developers tend to assume things like “software is good”, “metrics are useful”, and “following the process is valuable”. Leery developers are more likely to worry software is becoming more fragile over time, that metrics without context lead to abuse, and that rigid processes can create more waste than not.
Trait #4 - Economical vs. thorough
This was the most extreme result, and a few Test Double agents suggested that the skew is partly because this talk was presented before a predominantly Rubyist audience. Ruby and Rails, each being well over 10 years old, are at a state of maturity where thoroughness is both typical and important to long-term success.
Had we presented this at a Node.js or React conference, where a larger proportion of the audience’s lived experience was at an earlier stage of the ecosystem maturity curve, we probably would have seen a different outcome.
What this bucket measures is the extent to which a developer insists on baking in code “quality” (whatever “quality” means) even if it slows them down, as opposed to preferring to get code out the door as quickly as possible by shedding affordances like tests and buy-in from others. As with the other buckets, there’s no correct answer. Economical developers are who you want for rapid-prototyping or when the code’s primary value is to solicit feedback. Thorough programmers, meanwhile, are well-suited for deftly navigating complex systems that can’t afford a “move fast and break things” approach.
Next steps
If this talk resonated with you, I’d encourage you to spend some time tuning the same sort of feedback loops in your own work. After you’ve had some time to reflect and tweak your process, share it with others! I’d love to hear about it via email.
If you’re at a point in your journey where you need to find an employer who’ll trust you with the level of autonomy needed to take these steps, that’s, well, why we created Test Double in the first place! We’d be happy to talk to you about joining our team.
Transcript of the talk
[00:00:00] My name is Searls. My non-Twitter name is Justin. Feel free to call me either. This is what my face looked like in 2011, and thanks to how social media branding works, I'm now stuck with it forever. I work for a company called Test Double. We're a software agency who's on a mission to improve how the world writes software.
[00:00:19] You can learn more about us up at that URL. The title of this presentation is How to Program, and it's a rumination on a word, workflow, two part word, work being what programs are, their structure and behavior, flow being how we program our thoughts and actions. And when I look back on my experience learning as a computer science student, they taught me things like NP and big O analysis and cryptography, and not very much about how to think or how to work.
[00:00:52] And boot schools nowadays are, actually analogous, even though they're more market practical skills, like web standards and system tooling and languages and frameworks, not so much how to think through problems and solve stuff and really write code. And you might think that's the job of thought leaders, because the word thought is right there, but really thought leaders, when they're talking about design patterns or solid principles, even when they talk about agile and test driven development, Those are nice because they describe work activities, but it's pretty discreet and mostly about how people interact, not so much how to think through things.
[00:01:25] So it's reasonable to ask, when do we actually learn flow as programmers? Who teaches us how to think? And if you're lucky, 10 years into your career, you'll stumble upon or somebody will show you, the only productivity tip that any of us have ever been taught, the Pomodoro technique,
[00:01:47] where you work for 20 minutes and then take a 3 minute break. It's really awesome. But honestly, it feels like you put 10 years of hard service in. To get a, 4 plastic pin. It's insulting that's the best that we have to offer. So sure, somehow we all learned what programs are, but I'd hazard a guess that most of us, nobody ever really taught us how to program.
[00:02:09] Look no further than a Google search, how to program. And you get a whole bunch of terrible results, starting with the traditional way of teaching people how to program, You start with nothing and then somebody shows you a completely finished example, the finished product, what the program should be. And then as for connecting it, it's good luck.
[00:02:25] Have fun. And every single computer science assignment that I had in college, really resembled the How to Draw an Owl comic, where you start with two circles and then go draw the rest of the owl.
[00:02:41] And I spent entire weekends cooped up in a lab trying to figure out, staring at a blank editor and no idea how to write code. And it was that moment that I realized that programming is almost a philosophical activity that happens mostly in our heads. Of course, we've now innovated quite a lot in programmer education since I was in college.
[00:03:00] Now we instead of just one big finished example, we've broken it up into two or three steps over the course of a book or a screencast. But very rarely does the prose or the explanation actually explain the thinking of how to make that thing work. More real. It might take years before you're able to imitate, even an example application from a book.
[00:03:21] But that word imitation stands out because I think that most of us are just imitating other programmers. We see somebody successful or well known, and we just try to do things like they seem to be doing things, and that's how we learn and get by. You can see that this is endemic in our society as programmers because we're really bad at how questions.
[00:03:40] If I ask, how do I know when to create a new method? When should I break this thing up into more than one thing? You get really unsophisticated responses. Methods should be about three lines long.
[00:03:53] And, when my wife, she likes telling this story, when she was in first grade, Becky she was told by a teacher that sentences were two lines long. And so then, dutifully, for the next several years until she was corrected, she just stamped a period at the end of every other line. And that's funny, because she's not an adult.
[00:04:16] And yet, here we are with these unsophisticated ideas like, Ah, methods should be about three lines long and no ability to communicate above and beyond that. But let's say in spite of all of this, somehow you write a really good program one day and you're really proud of it and really happy. And I ask you, okay, so what actions were productive or unproductive that led you to that point?
[00:04:38] Or what thoughts led you there? Which thought processes were successful or unsuccessful? Would you be able to answer those questions and I, most of us wouldn't be able to. And it leads to rampant insecurity from how we educate programmers to the work that we do, to the colleagues that we keep in the overall industry.
[00:04:54] Now 99 percent of the work that I've done as a professional programmer could be boiled down as a business person trying to get a spreadsheet onto the internet. And yet it's taken me 10 years or so to even become a merely competent programmer. That's Clearly, something's wrong in how we teach people to program.
[00:05:12] And this industry is 60 or 70 years old, but we're still searching for silver bullets. We always are externalizing the problem and hoping the next language or framework or library or process is going to suddenly make programming explicable. And it never works out. And think about that situation, where everyone's either making stuff up as they go or pretending they understand it.
[00:05:34] Who's going to succeed in that environment? And it's Genuinely brilliant people, and people with the overconfidence of having been told that they're brilliant their whole lives. Imagine that you don't look like other programmers, and you walk into a room, and you lack that privilege of having been told that you're brilliant by society.
[00:05:49] This is a terrifying line of work to walk into, because no one can actually explain And I think that if you want to make programming a more diverse and inclusive industry, we really need to solve this.
[00:06:07] And it's obvious that the industry has no idea how software works. Because they're constantly analogizing it to literally any other industry. Like construction, or like design, or manufacturing. And because of that, they control the handful of things that they do understand. Estimates and when people work and where they work instead of the true None of it, which is like how we think as software developers and how we solve problems.
[00:06:36] So how do we fix it? Fortunately yesterday at the keynote DHH offered us one solution
[00:06:46] But I'm gonna talk about a different one I'm gonna talk about feedback loops because programmers through compilation and through testing we're used to Establishing feedback loops to make forward progress and we can do the same things inside of our heads We're going to practice this today by reflecting on the actions we take, and whether they're successful or not, and how to improve our actions.
[00:07:07] You can do the same thing for feelings, and actually reflect on your emotional state, so that you can reinforce positive emotions, or mitigate negative ones. And, spoiler alert, you can actually think about thinking, and produce better thought processes that turn out to be more productive. This is really the path to programmer enlightenment, but I realize that we're starting from scratch here, we gotta walk before we can run.
[00:07:33] And Ask yourself, what do we do with teams that are so emotionally immature they struggle to even talk about feelings? We hand them crappy personality tests, like the Myers Briggs Type Indicator. If you're not familiar with the Myers Briggs, just know that it's the worst type system.
[00:07:53] The reason we rag on it is because it puts people into these silly buckets like ENTJ and ISFP, and the implication is that there's only 16 types of people out there, but we know that there's much, much more. But when you're starting from zero, 16 starts sounding pretty good. So that's why today I'm pleased to announce the Searls Briggs Type Indicator.
[00:08:14] And instead of pontificating to you today about how to program and dictating that this is the magical way, this is the silver bullet, instead I'm just going to humbly take my own test, show you how I feel and my inclinations and my personality, and what I've done over the course of my career to reflect and improve and result in better outcomes as a programmer.
[00:08:34] To do that we need an example feature, so let's make one up. Like I mentioned I work at a company test double and we have always been a distributed company But we're still learning that does not mean evenly distributed So if you've got a flat organization, you might think that there's all these spontaneous relationships that form But of course that's not accurate We all have our assigned pairs and we all phone home to an account manager Our org chart looks like one of those 1980s suction cup ball things But there's nothing wrong with that per se unless, say, two people on the team both really want to learn Elm.
[00:09:06] But there's nothing systemically that's going to get those two people talking together necessarily. So somebody raised the idea, why don't we have virtual coffee dates on the team and just randomly assign people to talk to each other that otherwise wouldn't be. It's actually an expression from math, it's called the handshake problem which just calculates the number of potential relationships of any group of people, and that there's just tons of them.
[00:09:29] And so what the system should do is just send an email every week to pair up people who might not otherwise be talking to one another and tell them, go spend 15 minutes on this Google Hangout URL and chat about something, it doesn't have to be about work. And so we're gonna put my test to the test and build this feature together this morning as a group.
[00:09:46] And the first bucket that I'm going to put us in is sensitive versus fearless. And the first question is, I prefer hearing all requirements up front, even if I can't tackle them all right away. I strongly disagree with that. I get overwhelmed really easily. Two, adding to a long function feels like more code just won't fit.
[00:10:06] Absolutely. I once I hit a certain amount of complexity, I can't imagine adding another case. Three, I look forward to being assigned to new projects and teams. Thank you. No way, new projects give me night sweats. Four, I often feel paralyzed while staring at a blank editor screen. Yeah, I already admitted to that.
[00:10:25] So does that make me sensitive or fearless? Pretty obvious in this case, I'm sensitive. A big part of being sensitive is that I get overwhelmed easily. So think about this feature and all I've gotta do, create pairs. Great. That's not so hard, but I do have to go and send the email and I also have to look up all these people from a database, but I gotta be careful not to repeat.
[00:10:43] So I gotta randomize the pairings and I gotta not repeat week to week either, which means I also have to persist them. So there's a lot of work. So I. I don't want to think about all that. I just want to focus on the core problem. So my inclination is to like, just do that, put a unit around it, think hard about the problem.
[00:10:58] I could do my little test driven development thing. And I'm feeling really good about that unit of code. So then I go to the controller that's going to call it, and I realize that the method signature doesn't quite line up. Or somebody else might say, Hey this unit you just made is doing all this extra redundant work that's actually handled elsewhere.
[00:11:15] My inclination was to overcome that paralysis that I felt and find some productivity by just putting on blinders. And I fell into that rabbit hole often enough that I had to think and reflect and actually have inverted how I work as a programmer. So what I just showed you is often called bottom up programming, but now I practice top down.
[00:11:31] It works better for me. You might also call it outside in. Where I think from the perspective of the controller, or the top level entry point, and I start there, and I ask what do I need? I need something to create these pairs, and when your brain is focused that way, the caller knows exactly what inputs are available, what output it's gonna want, and it's also a way to minimize waste, because And it has the broader context in terms of, what guard clauses need to be inserted or can be omitted.
[00:11:58] The other aspect of being sensitive is that I'm really afraid of failure. And when you work outside in, you do have to juggle all these different concerns at once, which can, again, be overwhelming. What I try to do is just rush into solving it. My inclination is just to open up an editor and prove that I can write this code.
[00:12:14] So I start with a module, and I make a method, I go load up a bunch of users, I loop over them, I skip anyone who's already had their hand shaken. Otherwise, I'll go and add a tuple of represent a pairing, and then I'll go and slam it through some mailer. And now I feel really good, because I just proved I could do it.
[00:12:30] And someone will remind me, you remember you got to randomize it and you don't want to leave out the 15th person every week. And you also got to prevent repeats from happening week to week. And now I'm terrified because I can, this is already really dense, I don't know how I'm going to make it more complicated.
[00:12:43] And I'm very familiar with this corner of the room as a result. So I keep painting myself into this corner, and the root causes fear. I'm afraid of failing. I'm afraid of big things that I don't know how to break down. So my solution is avoid that fear by breaking things down in a systematized way.
[00:13:00] Give myself time. Some help and I've been practicing over the last two years iterating on an approach to test driven development that I call discovery testing And it's really an effort in breaking big scary problems into smaller more manageable ones It works with I start with just a test of that top level thing.
[00:13:17] So I write a single test case I invoke the thing and then I ask myself a crucial question What's the code that I wish I had, that I could defer and hand this work out to? Something to find these hands, something to determine who shakes whose hand, something to mail them and then persist them. Then I use my test double library, which in Ruby is GIMME.
[00:13:35] So it creates these four fake things and I set up a stubbing. So I say, hey, when finds hands is called, it gets you this thing to symbolize the hands. And if I pass that to the thing that determines the shakes, another stubbing will give me these handshakes. This is all the test setup I need to be able to actually assert That I mail out all those handshakes and that I persist them.
[00:13:55] Now this is an unusual looking test to a lot of you, I'm sure. But what it does is it perfectly specifies the behavior of that top level unit. And so I get this test to pass, and I never have to worry about that top level again. In fact, if you list out all the files that shake out from just getting that test to pass.
[00:14:10] Now I have a pretty clear work cut out for me. I know exactly what I need to do. I can start making forward progress. So I start off with a big scary thing, but as I build that test, I identify the units that I would need to actually carve out the work. And instead of one big scary thing, I now have four more digestible problems that I can focus on.
[00:14:29] And if any of them are scary, I now have in my back pocket, A tool that I can use to reduce and break things up even further. The second bucket I'm going to talk about is inventive versus aesthetic. So some quiz questions to determine my type. Question one, it's more important to build the right thing than to build the thing right.
[00:14:49] Eh, these are both important, but I'm more implementation focused. Question two, I love experimenting with new tools, frameworks, and build systems. Not at all. I do spend a lot of my time in open source here, but it's expressly so I don't have to worry about it at work. Question three, I strive to write visually appealing code down to syntax and symmetry.
[00:15:07] Absolutely. I don't know why, but I really like pretty symmetrical code. Question four, it's boring when all the code in a project is structured similarly. I disagree. I really like consistency in code. So does that make me inventive or aesthetic? I think it makes me a little aesthetic. And one part of being aesthetic is I have refined taste.
[00:15:27] Now the problem with taste is that nobody knows what it is. Until somebody on your team says, You know what, I'd prefer a 300 line function to all these well named little small units that you keep creating. And then my face looks like this.
[00:15:46] That's taste.
[00:15:50] And so why do programmers develop taste? It's obvious when you're staring at a blank editor screen, there are infinitely many ways to solve any given problem. We need something to constrain ourselves. Some patterns to follow, to just not be stuck in analysis paralysis forever. And that's why I think that prose is a much better analogy to writing software than construction is.
[00:16:12] In fact, I think of programming as just communication to the next developer who's going to pick it up. And it's just hard because it has to happen through this filter that's just formal enough for an interpreter or a compiler to understand. And when I hear this feedback, I reflect, and I take it to mean as a critique, That maybe I'm writing code that's meant to be read by myself, as opposed to another human.
[00:16:34] Because the other developer, they're not in the room. So I tend to write code that I think that I would want to read. And this leads to self centered design. If I just write a book on design patterns, I'm going to create a bunch of units like services, and factories, and repositories. And I'm going to feel really brilliant having done all this.
[00:16:50] But then, somebody else could list out all of the files I just created, in one big listing. From their perspective, it's going to be impenetrably complex. It's a forest of all these objects, no idea how things are going to work. And so now when I hear that critique, they've got a pretty good point. And I found this happened on team after team.
[00:17:09] And what I really realized is I got to start working differently to make my code more discoverable and approachable for other developers. And now I do things much differently. It started with realizing programs are directed graphs. So all of these units are really nodes in this graph and all of the edges are vertices.
[00:17:27] function calls. So the locator calls the service, which calls this repository, which calls this mapper, which instantiates hands. Service also calls this factory, which calls that thing and that thing. And we already, because we think of programs this way, we have some taste, we have some constraining principles that we share.
[00:17:43] Like for instance, if this repository were to call this service, We would all, wait a second, that's a dependency cycle. We'd realize there's a risk of a infinite recursion or stack overflow there. And so most of the programs that we write as developers try to be acyclic digraphs.
[00:17:58] And remember, my purpose here is just to prove that I'm right. I want to tell this person, no, small units is better than 300 lines objectively. And to do that, I thought, maybe there's a liberating constraint. Maybe if I refine my taste further, I can solve this problem. And where I landed was, I want to express all of my features, not as just general graphs, but as trees.
[00:18:19] Because a tree is just a special subtype of a graph. You can take this exact same menagerie of units, And organize it in a tree shape, where you have your value objects on the left and the function feature behavior on the right. And now, if it's true that I have any needless indirection, it stands out in a tree because it's just got one child.
[00:18:36] So that service locator, yeah, I can probably get rid of that. And if somebody asks, what they're looking for is how to compare two hands, they can search the tree much more easily and quickly than just a gigantic directory of files and find what they're looking for. So it's more discoverable.
[00:18:52] The other aspect of my aesthetic is I'm a minimalist. So when you look at our app, you see it's a car, it drives, you think about what its function is and why it exists. When I look at our app, all I see is the clutter and the mess. And as a minimalist, my productivity goes up when things are tidy and symmetrical and terse, and it goes down when things are cluttered or inconsistent.
[00:19:16] And what the software is supposed to be doing is its essential complexity, Everything it actually does is its incidental complexity, the other stuff. Think of everything that goes into writing a program. You're writing deploy tooling, and app config, and dependencies, and framework appeasement. Then of course there's whatever the app was supposed to do.
[00:19:36] There's style rules, continuous integration, build systems, and then, of course, unnecessary stuff. As a minimalist, I'm the person on the project who's always chiseling away to try to minimize the amount of incidental complexity in the system, and trying to maximize the time that the team spends on whatever is really important.
[00:19:54] That means I'm always tweaking my code style. Like maybe I'd start writing feature behavior inside of my model objects, but then separate them out into separate units, but then maybe make them callables, but maybe ultimately land at module methods. These are all fine. You can debate the finer points, but at the end of the day, they're really six of one, half a dozen of the other kinds of arguments.
[00:20:13] And they represent a sort of trap. Because earlier I said style rules, debating style is a type of incidental complexity, Because style is subjective, and so it changes around arbitrarily. And arbitrary decisions breed inconsistency, and oh, it turns out that inconsistency is another form of incidental complexity.
[00:20:32] And oops. Because if on Monday I organize code this way, and on Tuesday this way, and Wednesday that way, and then by Thursday I've decided this is the best way to organize code, everyone's gonna get mad at me, because I've just littered 36 custom little files all throughout the system. So my temptation to continuously be improving stuff actually breeds inconsistency and creates bigger messes elsewhere.
[00:20:55] So I reflected on how I think what was happening is I chased the local optimization at the expense of the global optimization of what's best for the overall project. And I had to learn to avoid this oscillation in my design. And it really required me to just realize I'd spend the entire project spinning my tires.
[00:21:14] And so instead, I decided I need to just lock in these decisions and say, We're just going to do things this style, whether or not it's better or worse, it doesn't matter. So we can try to carve out time to be productive. And as I got better at that. At flexing that muscle, I could recognize when I'm spinning my wheels earlier and spend more of my time being productive.
[00:21:31] It was a way for me to both be minimalist, but also really consistent in my applications, even when it meant hewing really strongly to arbitrary decisions of things that didn't really matter. And when you do that, especially with your incidental complexity, it brings the essential complexity into sharper relief, so you can all as a team really just focus, spend more time on what your app really needs to do.
[00:21:55] The third bucket is naive versus leery. Question one, publishing metrics like code coverage is always a good idea. And I think radical transparency often backfires. Question two, writing good messages today will pay off in the future. Secret. I don't actually read commit messages yeah. Three, software teams will make smarter use of time under pressure.
[00:22:18] Disagree. I think pressure kills cognition. Four, software is generally improving over time and we are not doomed.
[00:22:32] Pass. Does that make me naive or leery? I'm starting to realize this is a pretty obvious test in my case. I'm leery. It all starts with my distrust of all of you. Because most teams operate in a pressure cooker. They're under pressure to get as much stuff done as fast as possible, and as a result, their brains turn off and it, results in really bad outcomes.
[00:22:54] In fact, it's not very fair to pressure cookers because pressure cookers serve a useful purpose. So I'll find another analogy.
[00:23:05] Where we're just being squeezed for all of the Ruby and JavaScript that we're worth. And again, not being creative, not resulting in good code. One person on teams I don't trust is I don't trust product owners. I love them, but I don't trust them. Back in the waterfall days, a product owner would be able to specify 300 bullet points of everything that they ever wanted and then foist them upon us and we'd say, hey, this is awful.
[00:23:30] In a way, at least it was honest. They got to articulate everything that they wanted up front. It was just us who didn't know how to handle that much complexity. Scrum and Agile stuff gave us a little bit of a backbone and we said, no sir, you only get one index card at a time. And in fact, that's going to be 20 points.
[00:23:46] And I made up what points mean. And so naturally then it becomes a debate of, no, I think it's five points. Any system can be gamed and really savvy product owners I know are really good at this. So you're like, Oh, this is not that complex. It's just a little cartoon whale. You guys can do that, right?
[00:24:05] You have time. And we'll say, Oh yeah, sure we can do that. And then we realize it's much more complex than we really thought. And by then they're out to lunch and we're left holding the bag. So I don't trust product owners. Of course, someone I trust even less than product owners is other developers. And it's because on day one of a feature, we neglect to realize a very basic fact.
[00:24:28] Our brains can only hold so much stuff in them at once. And so I think the developers have this bias towards sizing features to just whatever number of things they can hold in their head at a time. And naturally, the conclusion that we draw about how big our objects and methods should be is the same size.
[00:24:45] Because then that way we can just put all the things in one place. It seems like the simple solution. But then a month passes and somebody says, Hey, you need to add a couple additional aspects to this feature. It has to do this and this as well. That means that the other stuff, like our brain is finite.
[00:25:02] We can't, keep it all in our heads at once anymore. But units, we can make files as long as we want. So we just, even though we're incurring a paging cost we can slam in those additional attributes to the feature. But it creates a blind spot for us where, now that we're not thinking about the persistence, bugs can creep in through that door.
[00:25:21] And if you work this way, a year passes and pretty soon your units are just gigantic and they look like this and they're riddled with bugs. And normally day 400 is the day that I get a phone call saying, Hey, can you help improve our tests? Of course, this isn't a testing problem, right? It's like a complexity management problem.
[00:25:38] And it's hard for me when I see this cycle repeat over and over again to let that distrust like. Grow into cynicism and that's not good. And in fact, if I'm empathetic, I realized that at the root of this industry, all of us really struggle to predict how complexity is going to change and to guess the complexity of stuff.
[00:25:55] And I do it too. And so what I want to do is change the question and instead focus on how to prepare myself and other people for the inevitable increase of complexity, because on any maintained system, almost any of them, they're Complexity is going to go up over time. It's just a matter of what that graph looks like.
[00:26:12] And so like Pascal, I made up Searle's Wager in my head. Where, sure, complexity might remain constant, or it might go up. And yeah, we could keep writing these brain sized units, or much smaller ones. And if complexity doesn't change, no harm, no foul, it doesn't really matter how we factor our code.
[00:26:29] But if it goes up, we have a lot of evidence that these larger units cause problems. And the nice thing about small ones is that they can actually accommodate some additional complexity without too much pain. And that's why on day one of every feature, I break things up into itty bitty tiny units. To the point where people criticize me and, if you think that my units are too small, don't worry.
[00:26:49] Because I trust that you're going to go and make them bigger later. It'll work out. It's a great way if you struggle with trying to make a follow the single responsibility principle and have every object do one thing This makes it really easy in fact This is what the tree of functionality shook out as in this example and every single unit serves exactly one purpose and they all Follow one of three roles that I've observed over the years of practicing this the parent nodes are delegator objects They don't really have any logic or branching, maybe just like one if condition or something.
[00:27:23] They mostly just break up the work and hand it off to other things. And the way that I happen to do that is I use this test driven design where I use test doubles to identify and think up what's, what's the code I wish I had that would actually do the real work. What you want to try to maximize is these leaf nodes.
[00:27:39] So this is where the core logic of the application is. It takes inputs and transforms it to some kind of output. These are pure functions, so you don't need test doubles. You just are actually testing real logic. These are the kinds of unit tests that everyone likes to write. And it's like functional programming for people who don't want to think too hard about functional programming.
[00:27:57] It makes it very accessible because you can still do it in, Ruby. You don't have to change languages or something. On the left are my values. And value objects, they just wrap a little bit of data. Maybe they just hold onto a hash or an array. And the methods on them are only allowed to elucidate the data and answer questions about the data, as opposed to doing feature work.
[00:28:17] They're not there to actually build features. Instead, I think of them as the sludge that flows through the pipes of my feature code. They're the types in those method signatures. And even if all this abstraction doesn't ultimately pay off, and even if that feature doesn't change a lot in the future, the very worst case, I've got a very discoverable system of carefully named things that are obvious and small and comprehensible.
[00:28:39] So it's not the worst outcome in the world. The third aspect here is that I distrust myself even more than all of you. It's because I worry about the future and even though I'm not super confident in today me's skills, I'm even more worried that my future self won't be able to program either for some reason.
[00:28:58] And so when I'm writing a feature, somebody says, build this thing, I'll think about how to build it. And then I'll build a message in a bottle for myself in the form of better tests and documentation and commit messages, things to try to help. And my future self, who's wearing sunglasses, Is told to change that feature.
[00:29:15] He gets really frustrated because what those tests mean is now he's got a bunch of other stuff to do every time he wants to make a change. And my future self would much rather just start fresh and be able to write new code. And so I kept trying to do myself favors by, by, by writing a lot of extra tests and adding in all this quality at the beginning, but it would actually tie my hands.
[00:29:34] And so I had to reflect, how do I bake quality into my applications? Without creating a, an undue burden for my future self. And so I started working a little bit differently. Because if this is our tree of functionality and our manager comes in and says, New requirement. Thanks to some HR fiasco, all handshakes have to happen between three people.
[00:29:55] The traditional way to solve this is to carefully read the existing code. Add, remove, change, test, change the code, and then try to make as small of a mess as possible. And I say small mess because any time the purpose of a piece of code has been two things whenever we change a unit, it carries with it technical debt.
[00:30:17] It confuses the story of why it existed, because it's changed over time. So instead I've been trying to make my code disposable. And what that means is that when I look at this tree, I search for all of the affected units by the change. These are two units that are going to change. And so then I find the smallest subtree that encapsulates that change.
[00:30:37] And so there it is right there. And then I do something unusual. I blow it all up. I just knock it out because I trust that my future self is going to be able to see that top level thing and understand what the contract is. So there he is. He's going to drive out a new solution. And as opposed to just changing these old units that implemented the logic the old way and thus rack up technical debt.
[00:30:57] So I trust him to drive out a new solution. And in fact, Future us is gonna have more experience than present day us. They'll probably have a better understanding of the business. They'll be able to write better units in the future than we ever could hope to today. So this is a healthy way to work. It does mean that I try not to reuse code too much in my feature code because any code reuse, like if you have a method that's called in nine places, it's really hard to change that method because you have to consider nine different place callers and what they need.
[00:31:25] It's also really hard to throw it away and replace it. So I try not to reuse too much code. And it also forced me to let go of this idea that maintainable code has to live forever. In fact, as opposed to that, this incremental rewrite in the small as part of your process is a way to pay technical debt, without saving a rainy day fund for when you finally get to refactor.
[00:31:46] And, And in doing that, I actually made myself happier because future me does not want his job to be, fixing all of Justin's old janky tests every time that he changes something. He wants to be able to write code for a living. And so this process by making death a part of life while we're working through features is a great way to keep your teams happy, I've found.
[00:32:08] The last bucket here is economical versus thorough. The question one, better to ship code quickly than wait until everything is tested. That's it. And I feel like I'd just be bailing out water in that case. Two, design principles are useful, but most teams waste too much time on them. It's possible, for sure, but few teams are at risk of this.
[00:32:26] Three, most teams lack a sufficient understanding of their dependencies. Absolutely, 90 percent of us have no clue how most of our code works in our applications. And four, it's okay for everyone on a team to maintain separate coding styles. I actually strongly disagree because this leads to siloed development.
[00:32:43] It's like a Conway's Law of style. So does that make me economical or thorough? Of course it makes me thorough. Some of you are noticing that this spells salt. That's because when you make up the quiz you can make it spell whatever you want. It's going to surprise a few of you to learn today I have a confession to make.
[00:33:04] I'm a bit of a control freak. And That means I'm dubious of free stuff. So when you see a sign that says free puppies, all I read is extra work. And another thing in our industry that makes me think extra work is open source. And so just keep that in your head next time you're on github asking for free labor from an open source maintainer.
[00:33:24] Imagine you're yelling at a puppy instead because who yells at puppies, you jerk.
[00:33:32] And open source isn't free because just like puppies we have to learn how to use it. We have to learn how it's changing and follow it. And it's not just this panacea that we can pull off the internet and instead of having to build stuff ourselves. And if that open source has a bug, in theory, sure, we can fix it ourselves, but in practice, we're not going to understand all that stuff.
[00:33:52] We're probably going to have to rely on a maintainer or an expert to come and fix it later. And finally if we have let's say that this is our graph of objects and it's all very consistent everywhere. And we slam in some third party API, that's going to create a certain amount of friction and pain because it's not going to look like all of our other objects.
[00:34:10] In fact, third party dependencies have this really nasty habit of leaking references all over our code base which makes it really hard to change or replace or upgrade those third party dependencies over time. So my temptation of all these negative things about open source and puppies is to I don't have a puppy, first of all, again, much to my wife Becky's consternation, but two, I try to avoid using open source.
[00:34:35] That's what my gut tells me to do. But that's increasingly untenable these days. Cause I'd be reinventing wheels constantly. So I had to change how I thought about open source instead to maybe protecting myself from its blast radius, as opposed to avoiding it entirely. And the way that I do that is I write wrappers of all the third party code that I write.
[00:34:53] In fact, we already have one up on this tree. It's that finds hands unit on the left. So this is a wrapper object and it starts as just a well named delegator. Very exciting. No op, it doesn't do anything interesting. But it comes to encode all of our understanding of that dependency. It is the carpet under which we sweep all of the stuff that we have to do to make that third party dependency happy.
[00:35:12] And I integration test it, but only in proportion to how suspicious I am of it breaking. Otherwise, I trust that it itself is tested well. And it might feel like needless indirection, but I need you to trust me that it's not. Of course, if this is where we're calling finds hands in our top level code, and I just told you not to trust other developers, so you don't trust me you assume that that wrapper is unnecessary, let's say, and instead of using that call, we're just gonna call user.
[00:35:36] all instead. That way it's much more direct. We got rid of that needless indirection. Which means we have to update our test. So this is our original test. We can get rid of that fake finder and instead have an array of real users that we create in the database. Of course validation is important.
[00:35:51] Failed. So now I have to add like a birth date, some incidental other arbitrary thing to make this work. I can also get rid of the stubbing here and pass real users into the other stubbing. And I gotta make an integration test as opposed to a unit test. And ask yourself now, What did we just do? Cause the value of this test used to be crystal clear.
[00:36:11] It's purpose was to break the work up into four clear responsibilities. And now what is it? It's an, it's calling through to the database so it's making sure that the thing works but only when three quarters of the code paths are fake? That doesn't seem right. That seems wrong to me. And it's really becomes obviously wrong when this becomes more complex, because if we add a couple of scopes, like only search for full time and active employees, then our test isn't enough anymore because those scopes are like branches.
[00:36:38] So now we need two or three test cases to cover everything. And now it's abundantly obvious that we sliced things wrong. So when I hear teams complain, you have too many abstractions or too many objects. Typically that, I think that's, it's not wrong, it's not that you're making not an too many abstractions or that no abstraction is somehow better.
[00:36:57] It's that you're probably mixing the levels of abstraction in your system because life with wrappers is much easier. If I look at that same thing and dive into what that wrapper looks like, yes, on day one it looks like needless indirection. But on day two when it gets more complex, there is a, you, it's easy to write a test for this.
[00:37:13] There's a place for that complexity to go. And over time, it might become a little bit more complicated as you go. Distance yourself further here with a transformer to a type that you own as opposed to you know an active record user So let's say our boss comes in and says we're going to change from active record to the sequel gem Traditionally this would really panic everyone in the room because you're gonna have a billion references to active record all over your system But when you're writing wrappers carefully of your third party dependencies, you're actually preventing Preparing for yourself an adapter interface that's minimal and specifies exactly how you use that dependency.
[00:37:49] So you can even answer questions like, would it be possible to switch to this third party thing? And if you did, you could create an alternate implementation and run both in parallel for a while. It's a much, much better way to work. And what I found is, it's a way to maintain still have all that convenience of open source and sucking in these useful libraries, While maintaining control over how you work.
[00:38:08] When you're as introspective about this stuff as I am, you run the risk of explaining the universe through yourself. In fact, if you ask anyone who's ever worked on a project with me what my favorite way to write code is, it's my way. And I'll fight you for it. So my inclination is to just work really hard to convince you all that my way is best.
[00:38:28] And I'd love to say that's an altruistic thing and I want you all to be better programmers, but it's not. It's selfish. What it really is I'm afraid I'm gonna have a manager someday tell me that I can't write code my favorite way anymore. And, when I reflect on that, even if I were to come up with the perfect way to write code and hand it to you, I would run the risk of robbing you of that same autonomy, and so that wouldn't be good either.
[00:38:51] And so that's why I'm getting out of the silver bullet business. So even though I talked a lot today about how I program, I'm not here to sell you on that. I hope you find some of it useful. But what I really am here to sell you on is the idea of pausing and introspecting about how you act.
[00:39:06] Feel and think while you're programming, so that it becomes more explicable to you, and then you can articulate it to other people, and improve, and share. And you might ask yourself, hey, if we all start thought leading ourselves, won't that just create chaos on our teams? And I think that's actually a valid concern, so let's spend a minute to talk about that.
[00:39:24] Because earlier I said, If you lock down all those arbitrary decisions up front, you can spend more time being productive. That's true individually, but it's also true as teams. And that's why when you have disagreements with other people on a team, you should aggressively pull those forward because the earlier you have that discussion and say, okay, we'll use these semicolons or we'll follow this style, if you can agree to that stuff early and lock it in, you'll as a team be more productive.
[00:39:47] And in fact, if you look at the average team, remember what I said earlier, we're all just a team. Imitating other programmers. So most teams actually like the idea of normalizing. In fact, some of them like it too much. Where if you have a creative idea of doing things differently, Ah, I don't want to rock the boat.
[00:40:03] I don't want to like, do my own thing off here or go off the reservation, quote unquote. So instead, We tend to just gravitate towards the lowest common denominator on a lot of teams, and that's not good. But that's not you anymore, right? Because after today you're gonna be thinking about how the actions you take could be improved, your feelings, improving your thought processes, and on your next team, when you join a team of all enlightened Hugh Jackmans, Some of whom will be salt, and some of whom will be fine, which is literally what the other four traits spell.
[00:40:36] And some will be sale, and some will be fault. When you look at this team, yeah, sure, they all have very strongly held opinions of different ways of doing things, but the one thing they have in common is when they're approached with a new idea, they have a system for testing it, and figuring out whether to adopt or reject it.
[00:40:54] And so they're not afraid of trying new things. And that's why I think that really introspective developers, when they're on a team, they might just look like they're talking at the whiteboard all day, but they can actually have a multiplicative impact on each other as they grow. And that's really my dream today, is that if we can, as an industry, normalize the concept of metacognition and self improvement so that we can explain how to program, this whole place might start making a whole lot more sense.
[00:41:22] And that is how to program. I appreciate your time here today. If you're looking for a company to work with that's going to support you on this kind of journey, I hope you'd consider checking us out. That's our page explaining what it's like to work with us at Test Double. And if you're a company that like is looking for additional developers on your team, I hope you'd consider working with us.
[00:41:41] We joined Other teams, just as additional developers with an interest in helping everyone get better as we go. And you can learn more about us on our site. By the way, I really made this quiz. It's a real thing. I actually printed out like a hundred copies and I have it in my bag. So you can get a special commemorative edition today.
[00:41:59] If you come up and say hi to me, I'll hand you one. I also have a lot of Test Double stickers. If you don't want to say hi to me in person, you can actually go to testdouble. com slash salt. And fill it out. We made a Google form and then two of our agents yesterday actually figured out how to automate Google forms so it'll calculate Your programmer type and email you right away.
[00:42:16] So I hope you go check that out too. But most importantly of all, I'm really thankful for this opportunity and for your time this morning. Thank you.