When building a new thing, a good first step is to build a thing that does nothing. That way, you at least know you are starting from a good place. If I’m building a component that performs an action, I’ll probably do it in these steps:
- Step zero is to write a standalone program to perform the action. This ensures that the action is even possible.
- Once I have working code to perform the action, I write a component that doesn’t perform an action. That at least makes sure I know how to build a component.
- Next, I register the component for the action, but have the
Invoke
method merely print the message “Yay!” to the debugger without doing anything else. This makes sure I know how to get the component to run at the proper time. - Next, I fill in the
Invoke
method with enough code to identify what action to perform and which object to perform it on, print that information to the debugger, and return without actually performing the action. This makes sure I can identify which action is supposed to be done. - Finally, I fill in the rest of the
Invoke
method to perform the action on the desired object. For this, I can copy/paste the already-debugged code from step zero.
Too often, I see relatively inexperienced developers dive in and start writing a big complex thing: Then they can’t even get it to compile because it’s so big and complex. They ask for help, saying, “I’m having trouble with this one line of code,” but as you study what they have written, you realize that this one line of code is hardly the problem. The program hasn’t even gotten to the point where it can comprehend the possibility of executing that line of code. I mutter to myself, “How did you let it get this bad?”
Start with something that does nothing. Make sure you can do nothing successfully. Only then should you start making changes so it starts doing something. That way, you know that any problems you have are related to your attempts to do something.
Thanks for the blog! 👍 This mindset is remarkably close to test-driven development’s red-green-refactor.
The first time I saw `–dry-run` as a possible argument to a command both impressed me and also made me scared to try it.
Providing some scaffolds or templates for initial step of the development helped me for this approach. Like SDKs, having some application development kits(ADK) in a team helps new developers to do required business more easily.
And I think within serverless application models or event-driven architectures, this is more legit to do “a thing that does nothing” first.
As for writing code in large unbiteable pieces, I’d blame the popularity of (a) languages and tools that “teach” newbies that slow half-an-hour compilation is something normal and (b) stuff like IntelliSense that pretends to find errors faster but is really good in finding non-existing errors and convincing newbies to trust it without even trying to really compile.
From my experience with students.
This sort of philosophy is also the reason why things like Test Driven Development mandates writing (and running) failing tests first. Because it helps you make sure you don’t have a test that actually fails.
Absolutely. That’s long been my standard approach to such things… it might not even be a test, strictly speaking, but a unit testing framework is a good starting point for adding some experimental code and some assertions, expanding it to cover more cases, etc. And once it looks good, you can extract the real code from the test, and you’ve got a nice little well-tested module to start using.
A common pattern I’ve seen is that a junior developer asks for help trying to debug why their code changes aren’t working. They spent a lot of time trying to alter their code, but in reality they haven’t even done a basic sanity check to test if their code isn’t running in the first place. Then it becomes an exercise in figuring out why the code isn’t getting executed (the equivalent of Raymond’s `Invoke` method).