Recent Posts (page 3 / 65)

by Leon Rosenshein

A look at 2023

snowman with falling snow animation
Happy New Year! Time for my yearly retrospective.

Big changes on the work front. I started the year working at Amazon on their Just Walk Out technology, but then got moved to a new initiative. The work was interesting, and from what I hear, going well, but Amazon and that project weren’t a good fit for me, so after a year at Amazon, I started looking around for something different. What I found wasn’t Amazon, but it wasn’t that different for me. I’m back at Aurora, now working on the Developer Experience team, focusing on software quality, bothg internal and external. I still get to walk to work, and I’m enjoying the challenges, and once again, I’m learning new things all the time.

With those work changes, I wasn’t able to do as much work on the blog as would have liked, only 50 new posts this year. It averages out to one a week, but it was way more bursty than that. My goal for this year is to be not just more productive on the blog, but also more consistent.

From a content standpoint, this year’s articles have been more about the what and the why, and less about they how. Oh sure, I have opinions on how everything should be done, and I’m happy to share them, but the posts have focused on what kinds of things you should be thinking about and doing and why they’re important, not how to do them.

With that said, here are the 10 most popular posts over the last 12 months:

  1. Careers Pt 3 – Visualizing Progressions
  2. Careers Pt 1 – Levels
  3. Effective Software Engineers
  4. McDonalds
  5. Incident Response
  6. Let Me Tell You What I Want
  7. High Quality Quality
  8. Seeing Like A State
  9. Breaker Breaker Rubber Duck
  10. 1 > 2 > 0

I’m really happy to see that my career posts are still getting good traction, as are the Eng Competencies. How you look at your career drives what you put into it, and from that flows what you get out of it. And to round things out, 2 more posted this year since #1 and #2 are from last year

Hopefully you’ve found those, and other, articles interesting and useful. I know I found writing them helpful in clarifying and cementing my own understanding of the topics. Until next year, Happy New Year, and remember to enjoy whatever you’re doing. We only get one shot at this life, so make it worthwhile.

by Leon Rosenshein

McDonald's

I’ve been a subscriber of Kent Beck’s Tidy First? since almost it’s beginning. If you don’t have a copy of the book, consider picking one up. There’s a lot of good stuff in there. Not just things to do, but when and why to do them.

Since then, Kent’s been talking about his Thinkies, which are questions (or statements) designed to induce movement and provide you with more options and help you reach a decision. They’re short, straightforward, and are simple to apply. You start with a pattern, the situation that triggers the thinky, apply the thinky, then look at what you come up with. In many cases you have an entire new way of looking at things, a roadblock or two has been removed, and you get to make forward progress.

One of the recent ones was called McDonalds. It’s a great way to get a group that’s stuck in analysis paralysis moving again. When the see the group is stuck, make a suggestion. Not the perfect thing, and not something impossible, but something that’s unpopular, no-one really wants, but not completely ridiculous. The group won’t take your suggestion, and you really don’t want them to, but very often the simple act of suggesting a concrete solution will get the group to come together on choice that makes sense to everyone and is a good starting point.

This thinky’s name comes from the situation. The group can’t decide where to go for dinner. So you make a suggestion, such as McDonalds. Someone then says that’s a terrible idea. Let’s go to XXX instead, and the group comes to consensus. Deadlock broken and everyone gets to eat dinner.

You can use it in lots of situations. How should we manage workflow for this new project? Lots of discussion around rolling your own, buying one, finding an OSS solution, and lots of discussion around the pros and cons (mostly the cons) of each option. After a while folks are just repeating themselves, and you stop getting closer to consensus. You’ve got the equivalent of a super-saturated solution, but it’s just sitting there. To get things moving, you put out a throwaway choice. Something like “I know. We can use Celery/Flask to manage things”.

Immediately, things start to crystalize out of the solution. Celery/Flask won’t work because something. Instead, we should use Temporal because it solves all of our problems. It’s like opening Schrodinger’s’s box. All of the potential and possibilities collapse into a concrete situation that you can then move forward with.

So next time you and your group find yourself stuck trying to make a decision, try the McDonald’s Thinky. And check out the rest of them as well.

by Leon Rosenshein

Strong opinions, Loosely Held

Everyone’s got opinions. Some strong we feel strongly about, some not so much. Some we are willing to consider changing our minds on, others we aren’t. How you balance how strong your opinion is and how hard you hold on to it is the challenge. It’s easy to hold on to something too tightly or for too long.

That’s why companies have policies like Disagree and Commit, Meritocracy and Toe Stepping, No Jerks, or No A**holes. It’s to encourage getting the balance right.

What’s supposed to happen is that everyone has their opinion. When there are conflicting opinions, people bring their opinions and their reasoning together and make an informed decision, that everyone then carries out together. It seems like a good idea, and when it works it is a good idea.

The problem is that, like everything else, it’s a two-edged sword. Those policies are supposed to help the best ideas rise to the top. However, they rely on certain conditions being true. That all the people involved are equals, and that there are no, or minimal, penalties for being wrong.

Unfortunately, that is often not true. Building software is a social endeavor. At its core, it’s about people and the way that they interact. And people have their own motivations and goals, and not everyone has the same ones.

What often happens is that instead of a strongly held idea with the best evidence being chosen, the idea with the strongest voice wins. Sometimes that is the loudest voice, sometimes the most persistent, sometimes it’s the most senior person’s idea. That’s not what we want.

And no policy that relies on others to keep it will make it happen. Instead, it’s on everyone, but especially those with more situational power, to make sure it does. By watching what is happening. By making sure that there is a counter to the loudest voice. An amplifier for the quieter voices. A supporter for those that need it. The longer I’ve been doing this, the more often I find that I have the opportunity to help other’s ideas get the consideration they deserve.

That doesn’t give anyone the option of not needing the facts, the documentation, the proof, that their opinion is good. What it does do is make sure that a good decision is made with the best available information.

That takes care of the Strong Opinions part. Which leaves us with the loosely held part. But that’s a discussion for another time.

by Leon Rosenshein

Stop Designing

Stop designing up front the moment you start speculating.

          – Kent Beck

That’s a pretty bold statement. After all, how much do you really know about what you’re going to need to do at the beginning? At the beginning of a project (or feature, or task, or bug investigation) all you know is a rough idea of the problem to solve, not how you’re going to solve it.

To me, what Kent is saying isn’t that you should only do design on things that are known/fixed/unchangeable, but that the level of detail of the design should match the level of detail of what you know.

As an example, if you’re in the US, and you need to be in England in 2 weeks for 3 days, making the decision to fly instead of going by ship matches the level of understanding. Adding detail to that decision, i.e., deciding what day/time to fly on, or which airport you’re going to fly into, requires more information that you have. That info is probably available, and easy to get, but deciding to leave at 8 AM on Monday morning before you know if you need to be ready to work Monday morning in England. Similarly, deciding to fly into Heathrow before you know that you need to be in Edinburgh is just going to cause you to have to change your tickets later.

It’s the same with software. You have the problem you want to solve. You have some understanding of the situation as it currently exists. You cad (and should) do a design to the same level of detail. A handful of boxes on a whiteboard and some lines and arrows connecting them. You might have persistent storage. You’ll probably have a few different major steps in the process of solving the problem. There’s an expected flow between the steps, and some places where you might need to back up or start again. If you’re like me, you’ve already thought of 15 different ways things can go wrong, so you know you’ll need a way to get information about a failure late in the process back (or forward) through the system. Given what you know, that’s about how much you can design. After that, you’re speculating. You’re guessing really. It might be an educated guess based on past experience, but it’s still a guess.

That’s the point to stop designing and start implementing. Build some APIs. Let some test data flow through the system. Hardcode some good and bad responses and see how the system responds. Adjust your APIs and data structures so things flow well in both happy and sad cases. Learn from the system you’re build. If something seems hard to do with your flow, it’s probably not you. It’s the system telling you that you’ve uncovered an emergent property, and you need adjust to work with it. Once you’ve got it working, look again. Do some refactoring and cleanup. Listen to what the system is telling you and adapt. It’s Test-Driven-Development at work. Or if you don’t believe in that, you can call it Domain Driven Design. Or Behavior Driven Development. Or even Extreme Programming.

It doesn’t matter what you call it. The important part is that you design to what you know, leaving room to adjust as you learn more. Then you learn a bit, design a bit, and do it all again. Rinse, Lather, Repeat. It’s more than a way to sell more shampoo.

Don’t worry (yet) if it can handle 10K transactions/sec or if it’s resilient to a backhoe taking out a datacenter. You might need that. You might even know you’ll need it for a production system, and you might be right, but you don’t need it yet. So don’t worry about it. Don’t preclude it, but don’t code yourself into a corner where you are stuck with a decision you made before you knew what tradeoffs you were making.

Beware of biases. There are lots of them. Sunk cost bias. You’ve put so much effort into making this thing work you don’t want to give it up. Recency bias. I just finished this, so I need to use it now. Overconfidence Bias. I know what I’m doing, and I know everything I need to know already, so my initial idea must be the right one.

So don’t design for things you don’t know. Or, as Jack Burton said, I never drive faster than I can see. You just need to develop the reflexes to know how fast that is.

by Leon Rosenshein

Break It Down, Even At Small Scale

I’ve talked about taking breaking things down many times. I think it’s a really good idea. I’ve talked about how it applies to commits, pull requests, and projects in general. It can help you move faster and be more responsive to both changing conditions and things you learn along the way.

It also applies at smaller scales. You can have one huge, long method, or you can break the method down into logical parts, with another method around it to manage data flow and decision making. It’s the Unix Way. Each method does one thing, then you have another method that chains them together. It’s easier to understand, easier to modify in the future, easier to debug, and easier to test. What’s not to like?

It applies at even smaller scales than methods. When you have a long computation or an involved condition, there’s always an urge to play code golf and shorten it. Sure, it can work. It might even take a few less characters. But you should resist the urge.

Instead, make the code longer. Just like you should break down long methods into multiple methods that do one logical thing, then use a wrapper method to manage control and data flow, you can break down long calculations. Use well named intermediate variables for storage. Create specific variables to hold the results of comparisons. Then, combine those intermediate variables as needed. If you do that, you’ll find that the result is more readable, more debuggable, and more flexible.

Consider this case I wrote about before.

var filteredEntries []userstate.StateEntry
initialTargetLen := len(targets)
for _, entry := range entries {
    req := entry.Value.(authutils.GetCredentialsRequest)
    // This is getting complicated, so breaking it down. We EXCLUDE a profile if ALL of these conditions are false
    // 1) The user specified a list of profiles and the current profile is one of them
    // 2) The user did not specify a list of profiles AND specified the --all flag OR the --aws flag
    // 3) The user did not specify a list of profiles, the current profile is not an admin profile,
    //       and the user did NOT specify the --admin flag
    // 4) The user did not specify a list of profiles, the current profile is an admin profile,
    //        and the user specified the --admin flag

    if !((initialTargetLen > 0 && targets[entry.Key]) ||
        (initialTargetLen == 0 && (refreshParams.Flags.All || refreshParams.Flags.Aws)) ||
        (initialTargetLen == 0 && roleIsAdmin(req.AWSRole) == refreshParams.Flags.Admin)) {
            continue
        }
    filteredEntries = append(filteredEntries, entry)
    delete(targets, entry.Key)
}

That’s a really complicated conditional, and every comparison is written in low-level terms. A more readable, understandable version might be something like

var filteredEntries []userstate.StateEntry
userSpecifiedList:= len(targets) > 0
refreshableRole := refreshParams.Flags.All || refreshParams.Flags.Aws

for _, entry := range entries {
    req := entry.Value.(authutils.GetCredentialsRequest)
    // This is getting complicated, so breaking it down. We EXCLUDE a profile if ALL of these conditions are false
    // 1) The user specified a list of profiles and the current profile is one of them
    // 2) The user did not specify a list of profiles AND specified the --all flag OR the --aws flag
    // 3) The user did not specify a list of profiles, the current profile is not an admin profile,
    //       and the user did NOT specify the --admin flag
    // 4) The user did not specify a list of profiles, the current profile is an admin profile,
    //        and the user specified the --admin flag
    entryExists := false
    if userSpecifiedList {
        entryExists = targets[entry.Key]
    }
    roleAdminFlagMatchesCLIFlag := roleIsAdmin(req.AWSRole) == refreshParams.Flags.Admin

    if !((  userSpecifiedList && entryExists ) ||
         ( !userSpecifiedList && refreshableRole ) ||
         ( !userSpecifiedList && roleAdminFlagMatchesCLIFlag )) {
            continue
        }
    filteredEntries = append(filteredEntries, entry)
    delete(targets, entry.Key)
}

Overall, it’s more code, but it’s been a looong time since the size of the source code has mattered much. And in the areas that matter to future me, it’s much better. The conditional is much less complex, it’s easier to understand because it reads much more like the English in the comment, and it’s much easier to add to if needed.

I never did make that change, but if I ever do go back into that code that’s the kind of change I’ll make.

by Leon Rosenshein

It's A Trap

Overheard:

… coming from another language and trying to find the library that makes it look like the language you came from is a trap

Pretty much all of the languages we write code in today are turing complete, meaning they can be used to write a program that does just about anything you want. That means all languages are equivalent, and the choice doesn’t matter, right?

Wrong

And it’s wrong on many levels. Consider this simple case. You’re going to spend a day or two, by yourself, writing a tool to do a job you need done now. Somehow, you know that once you finish writing it, you’re never going to touch it again. You won’t need to debug it later. You won’t need to understand it again later. You won’t need to update it later. No one else will ever see the code. Even in that case, your choice of language matters. As Larry Wall said,

Computer languages differ not so much in what they make possible, but in what they make easy.

That means your choice of languages is important even in the simplest situations.

Of course, we’re almost never in the simplest situation. You will need to debug the code. You will need to understand it later. You will need to update it. And you won’t be the only one who needs to do all those things. After all, your code needs to be readable and understandable not just by you when you’ve got all the context, but by someone else in the future without all the context. And it’s the future which influences the choice of language the most. The context that the code and the people that will be using/maintaining it will be operating in. If the tool you’re writing is going to be used in a front-end website and extend and connect with a large body of JavaScript, you should probably use JavaScript, not PHP.

Of course, choosing a language is only the first part. There’s always a tendency, especially when you’re learning the ins and outs of a new language, to just adjust to the new systax, but do things just like you’re used to doing them. After all, you can write code in just about any language that looks like the C code in the K & R book, and as I said, it might feel easier to write at the beginning.

Which gets back to that original thing I overheard. It’s a trap. Just because you can make one language look like another doesn’t mean you should. There are better choices for how to write things.

Languages have idioms. And ecosystems. When you write Python you should be pythonic. Code in Go should follow the Go Proverbs. C++ has its own set of idioms, and they’ve evolved over the years. Regardless of the language, you should understand and work with those idioms. The libraries, tools, and examples will be using those idioms. Working with them is going to be easier than trying to make it look like the last language you worked with. You should never be in a position where you’re working against the code or trying to coerce it into doing what you want. You never want to make things harder for yourself.

You should do it not just to make it easier to write, but because software development is a social activity. Working with those idioms will make things easier and clearer in the future. For people familiar with the language, it makes it reduces the cognitive load of reading it. Things make more sense, and you’re working with people’s expectations, not against them.

After all, you, and everyone else involved, are going to be living in the future, so have pity on future you.

by Leon Rosenshein

Visibility

I don’t often give career advice. Or rather, while I often talk about what good engineers and developers do, the importance of adding value, and the importance of managing your own career, I rarely talk about specific things you need to do to get a promotion. I’m of the opinion that if you grow and improve yourself, add value, and create and take advantage of opportunities along the way, your work will be recognized and valued.

That said, knowing how to manage your career is advice on how to get promoted. The difference though, and it’s an important one, is the motivation. Managing your career in a way that lets you learn and grow and expand your scope of influence means doing things that will help to get you promoted. When you do those things to learn and grow, they satisfy internal needs. The satisfy the need for Autonomy, Mastery, and Purpose. t’s good for you. It’s likely good got your team. It’s likely good for your company.

On the other hand, when you do things with the goal of getting promoted, you might learn and grow, but those are collateral benefits. Checking boxes on a level rubric can get you promoted. Unfortunately, when your focus is on checking those boxes, you end up not helping yourself, not helping your team, and not helping the company you’re working for reach its goals. Worst case you end up with what Charity Majors described as roving bands of skilled, restless engineers competing for vanity projects. And when you get the promotion, you’re likely to find that while it’s satisfying for a little while, it didn’t really satisfy that internal need, and instead of enjoying things, you just keep struggling for the next promotion.

The thing is, regardless of your motivation, you’ll do many of the same things. One of those things is ensuring you have visibility. Which brings me to this image, from workchronicles

I’ve mentioned workchronicles and shared one of their comics before. Like many of those comics, it’s more than a bit cynical, but there’s a kernel of truth hiding in there. You need to get past the tyranny of or. You do need to talk about what you’re doing and what you’ve done, but NOT at the expense of doing the work. Advancing your career requires a bunch of things. Of course, you need the ability. You need to demonstrate that you can do the work, but ability alone isn’t enough.

After all, your coworkers, your peers, have roughly the same ability as you do. They might have deeper knowledge of something specific, or a broader knowledge base, but they’re your peers. Assuming everything else is working the way it should (yes, I know what happens when you assume) if they’re your peers, your abilities are roughly equal.

Since ability isn’t enough, what’s the next thing? The next thing is that others need to know that you have the ability. That’s where visibility comes in. To manage your career, to set yourself up for growth, to be in a position where you can see opportunities, you need to make sure that your ability is visible. Or, as I said last week, you need to radiate information. The key is to make your ability (and the value you’ve added by using that ability) visible to people without bragging or forcing it down other’s throats.

You do that by informing people of what you’ve done, and how it helps them and makes their lives easier. You do it by asking relevant questions. Not to show how smart or observant you are, but to help others make the right decisions and to make sure they haven’t overlooked any potential problems. You do it by helping others and sharing your ability when it’s needed, and you can help. You do it by documenting and sharing your learnings. You do it by not being indispensable in an area, but by helping others learn that area themselves. You do it by doing the work that’s important and valuable and move things forward for everyone, because it’s important and valuable to everyone, not because it’s flashy, or “level appropriate”.

If you do those things your visibility will go up. Your scope of influence will go up. You’ll see bigger pictures and see more opportunities. Your autonomy, mastery, and purpose will go up. You’ll be more satisfied. And, as a side effect, you might get a promotion.

by Leon Rosenshein

Radiating Information

According to the Hacker Ethic, per Steven Levy in Hackers, all information should be free. Stewart Brand anthromophized it and noted the tension betweemm the cost of distributing information going down and the value of having the right information at the right time going up. He said

It seems like there’s a couple of interesting paradoxes we’re working with here. That’s why I’m especially interested in what Bob Wallace has done with PC-WRITE and what Andrew Flugelman did before that with PC-TALK. On the one hand information wants to be expensive, because it’s so valuable. The right information in the right place just changes your life. On the other hand, information wants to be free, because the cost of getting it out is getting lower and lower all the time. So you have these two fighting against each other.

So now you know where the idea that information should be free comes from.

The thing is, the information might want to be free, and there are those that will seek out the information, when they feel they need it, but that doesn’t say anything about people having the information they need when need it. Because regardless of what the information might want, it doesn’t have the ability to go out and find people.

On the other hand, we, as developers of systems and platforms, do have access to lots of information that wants to be free. And it’s up to us to make sure that information gets where it wants to be.

You’ve probably seen an error message like this

That’s not very helpful, is it. And the worst part is that the person who made that dialog box show up almost certainly knew more about the problem. They just chose to hide the information.

That’s a pretty obvious example of information being hidden by not being shown. Another way important information gets hidden is by being drowned out by less important information. Let’s say you’ve got a service (like a Wordpress blog with a security buddy plugin) that sends you the status of a security scan every day at 2:00 AM. And every day it sends you the same list of 7 files that have been replaced by the overnight backup. How long do you think it will take before you either just ignore that email or come up with a rule in your email client to delete it, or at least shuffle it off to an ignored folder? So that when one day the email says 10 files have been changed you never see it, let alone do anything about it. In that case, the information is free and available, but it’s just sitting there waiting for someone (you) to notice/care.

What that’s really pointing at is the difference between data, information, knowledge, and wisdom. We’re drowning in data. The key is to take that data, distill out the information in it, and present it as knowledge to the user. I don’t need 100 emails from all of the services I care about every day telling me they’re working fine. An email that one of them crashed and has restarted is useful information, but that can happen for lots of transient reasons out of my control, like a power outage, network drop, or hardware failure. That’s useful knowledge and I’ll care about it soon.

(Almost) Nobody reads automated status emails. What I really want is the knowledge that the service has tried to restart multiple times in the past 5 minutes, and hasn’t been able to reach a healthy state. That’s knowledge I need to respond to NOW.

The same applies to letting my users/customers know how things are going. They don’t want daily emails that everything is OK. But a red banner that shows up on every page of the website telling them about current problems/outages is knowledge they can use.

Or as my GPM Brad told me many moons ago, “Nobody reads the status email. I need to be alerted to the problem, so do that instead.” It doens’t matter how free the information is or wants to be. What matters is that the people who need to know it do know it when they need to. The value of having the right information at the right is incalcuble. Even if the cost to deliver a header or not deliver it, over the cost of the lifetime of a website, is esentially zero.

by Leon Rosenshein

Perspective

Perspective is important. Where you see things from changes what you see. Some things are visible from one perspective, but not visible from another. This applies not just to your physical location, but also to your mental location. Your experiences, your history, your expectations, and your biases all have a huge impact on not just what you see, but how you interpret what you see.

Consider this video of Saturn’s small army of moons moving around it. In the video the point of view (POV) is always focused on Saturn, and as you can tell from the fixed stars in the background, it’s always looking perpendicular to the plane of Saturn’s orbit around the Sun. In this case the moons move mostly across the image, and slight above/below Saturn, which always remains centered in the image.

Saturn’s Moons

In contrast, this video of the planets in our solar system moving around the Sun, starts out with the same POV, centered on the Sun, looking down from a point perpendicular to the ecliptic, the plane of the orbits around the Sun. It stays there until about 5 seconds into the video. Then it starts to move. The camera stays at the same distance from the Sun, but starts to move. It moves down towards the ecliptic, goes through it, moves along it, then ends up doing some kind of rotation in the same plane as the ecliptic.

Solar System

That looks like a very different kind of motion, doesn’t it? The planets are moving around, above, below, and sideways. Or at least they seem to.

In fact, the motion is effectively the same in both videos. And it’s the same as the motion in the first 5 seconds of the solar system video. The smaller objects revolve around the much more massive object in the center of the video. What makes the second one so complex looking is the changing perspective of the viewer.

It’s the same thing in software development. You see things with your perspective. If you’re responsible for the storage of data, you see things as rows in a table or key/value pairs. If you’re responsible for dataflows you might see things as pipeline or processing nodes with branches, tees, and connectors. From a user interface perspective, you might see views (input forms and controls) and the models (the hidden processing), and the resulting views (graphs, tables, gauges, animations, etc). All of those views are correct, but they’re not the whole thing.

Just as in the parable of the Blind Men and an Elephant, we often see things from our limited perspective, and miss the bigger picture. The trick is to overcome the bias of our perspective without losing our perspective. To take advantage of what we see from our individual POVs and combine that with other people’s POVs. Like everything else in development, how much to focus on one POV or another is a balance.

And just as important as balancing the different technical perspectives, is ensuring that we include other perspectives. There’s the customer perspective. There’s the short term business perspective. There’s the long term perspective. Which all gets back to understanding why you’re doing whatever it is you’re doing. Which is the most important perspective of all.

by Leon Rosenshein

Three More Tells

A while back I talked about The Three Tells. They weren’t poker tells. Instead, it was tell them what you’re going to tell them, tell it to them, then tell them what you told them. I thought it was a good way to approach a presentation then, and I still think it is today. But that’s old news. Today I’m going to tell you about three different tells. The three tells of Test Driven Development (TDD).

You might be wondering how TDD is like a presentation. I’m going to tell you how TDD is just a different implementation of the same formula. Let me explain.

The 1st tell – Tell them what you’re going to tell them

The first thing you do in TDD is write a test. You expect it to fail, and it does. Nothing surprising there. But what you’re really doing is telling yourself, your team, and the compiler, how you expect things to work. You’re telling them what the code you’re going to write is going to do. You’re telling them how you expect the code to be used. How and when you expect it to fail. And if you do it really well, you’re telling the people who will eventually use the code why they want to use the code and how it will make things better for them.

The 2nd tell – Tell it to them

The next thing you do is write the code. You tell yourself, your team, and the compiler exactly how do to what you want to do. You make the tests pass. You go into as much detail as needed. You might outline it then fill in the details. You might take the most important path and write that first. You might do the obvious parts first since you know what you want to say. You might dive deep in one area first because that’s where you’re least clear. And just like working on a presentation, you do some refactoring and moving things around to make it flow better. Until you’ve got it down to where it does everything you want it to and nothing more.

The 3rd Tell – Tell them what you told them

Once you’ve got the code written and the tests you wrote originally passing, you keep running the tests. As your understanding of the domain grows and you make changes. As others make changes to the same domain and other domains. As your understanding of the input data grows. You keep running the tests and reminding yourself, your team, and the compiler what you wanted the code to do. Reminding yourself why the code does what it does and why you wanted it to do it that way.

There you have it. TDD is just another presentation. Where you tell them what you’re going to tell them through your tests. Where tell them what you want to tell them by writing the code that makes the tests pass. And where, finally, you tell them what you told them by running the tests over and over again on each change, making sure that they still pass.