Building software is all about discovering work. You can map out the scope and plan the work before you start. And you should. But you won’t really know the specifics of what to build until you put her head down and start coding.
When you share your code for review, it should be in small, atomic pieces—easy to understand and review on their own. But don’t start there. Start with a hacky end-to-end prototype. Cut corners. Ignore edge cases. Just make it work.
This is the best way to discover work. Especially when working across the stack in an existing code base. Your assumptions about how things fit together will be tested. Your data model, for example, might have to change after you hit a roadblock on the front end. Better to find that out early.
After you’ve built it once. Build it again.
On the second time, slow down. Be intentional about writing quality code. Whereas the first build was messy and non-linear. The second build, with the benefit of hindsight, will be easier to ship in digestible pieces.
And yes, in practice this sometimes means duplicating work and writing the same features and functions twice. But the work from the first build can usually be reused. I’ll typically cut a prototype branch, knowing roughly what I want to achieve, and then just start building. Knowing it’s a prototype and for my eyes only lets me move quick.
Generally, I love a clean, linear commit history. But in these prototype branches I throw that out the window. For example, on a feature I’m building for Part3—Dynamic Stamps—my prototype commit history looks like this:
f2ad21f41 (HEAD -> dynamic-stamp-proto) Handle new stamps 1589d8ab0 Only add X on apply cae1ee095 Remove drag handle f9f6b14fc Conditional archive ce2e8ecd3 Use percentage for font size 83b8843fe Fixup mouse position 5a9edd91f Fixup some saving and archiving 3f53198dd WIP archive d0201bf94 WIP legacy and dynamic stamps; archive button 5627f9af7 Try to fix alignment on apply a532b7e09 WIP fix up apply behaviour 904f27d73 Add x type 97834110e Date to top; remove on apply 084275709 Font size; menu labels b003b0c7d More UI polish 514dc89fd Save handler af8e147f2 Update styling 628a79cda Move font controls 8b77618f1 Fix dragging a bit a1a2fc266 Fix drag rop 9d97cd2d2 Remove editing logic for now 61504c0bd Move buttons 2be284ffa Move font styles 94b567429 Don't use drag and drop fc29f5e62 font controls 2dc4b35fa Add selected state and conditional delete button a01eb4c3e Move files c53818064 WIP saving dynamic stamp 6768663ad Do some cleanup b8bc156a5 WIP use dynamic stamp in webviewer afd60f17d Fixsup stamp application 703b03623 Delete text fields 1d8f04b31 Prototype apply stamp ec4699280 Handle existing image f837b673c Different types e70d782a0 WIP first pass protoLots of WIP commits. Lots of fixes. Definitely non-linear. But I learned a ton while building. Where more work will be needed to cover edge cases and error handling (for those, I leave TODO comments). What the data model should be. How I should implement certain requirements. Missing aspects of the initial design. Etc.
The gist of this feature was adding variables to images (i.e. stamps) that can be auto populated when the stamp is applied. I started with font size controls for each variable in pixels. It was only when building the “apply” behaviour that I realized saving the font size as a percentage of the image size would be necessary. I probably would have missed this if I had fleshed out the “edit” behaviour in full before moving on to “apply”.
Once I have a working prototype, I like to push to GitHub and open a pull request just for me. It’s a helpful reference of all the changes. Then I will branch off the prototype, git reset all the changes (so I still have the changes, they are just unstaged). Then gradually edit and commit in a more digestible format. This is sometimes easier said than done, so it’s nice to have that prototype branch to refer back to if I need to rewrite something. As I encounter my TODO comments, I’ll create tasks (i.e. tickets) that I can come back to later.
Regardless of your tactics, build it twice. The second time you build will be easier. You’ve already discovered most of the work.