My ideal process ([1]) is...tedious. I never actually do it to that level, but my level of released bugs is low enough that what I actually do is good enough.

But what it does give me is a template to follow and adjust as the circumstances require.

So I always start with requirements. Requirements are my goals, and they are always in my mind throughout the rest of the process.

Then I design. I design the UI, UX, implementation, and tests, and I iterate between them until I get to a fixed point where everything is in a great local maximum.

If I encounter a local maximum that is bad or merely good, I throw out the design and start again from scratch and repeat as necessary. This is less necessary as the project ages because my intuition for the project increases.

After I have a design in hand, I do not start coding the feature. Instead, I figure out what technical debt the codebase now has.

That may not make sense, so let me explain.

Designing first means that I have a good idea of what needs to change in the codebase before the feature can be implemented easily. Before I wanted to implement the feature, the codebase had zero, or near zero, technical debt. But the intention to add the feature means that the codebase now has technical debt relative to where it should be for the feature.

So before I implement the feature, I tackle this new technical debt first, and when I am done, I use my full test suite, which has extremely high line, branch, and path coverage, to ensure that the refactor did not introduce new bugs.

Once I am satisfied with that, I implement the feature and add its tests. This includes adding as many asserts as I can to test the assumptions the code is making, both for documentation purposes and for quickly finding bugs with the test suite.

Then I get the tests passing.

Then I fuzz with AFL++, with the asserts turned on so that it will be as crash-happy as possible. This means that bugs are more easily found by AFL++, and it's easier for me to notice them too.

I fix the crashes found by AFL++ (most of them failing asserts), add the formerly crashing test cases to my test suite, and repeat.

Once AFL++ runs to "completion" (not finding any more test cases after having gone through basically all of the ones it found) without crashing, I have great confidence that the feature is robust.

But then I still run static analysis on it.

Then I add the feature to my NEWS.md file, increment the version appropriately, and put out a release.

However, woven throughout all of the steps is documentation. I document the requirements. I document the design. I document the new code, including a full Doxygen comment for every new function and type, as well as updated doc comments for any existing code that changed. I also comment the actual code heavily, using [2] as a model. I'll write down things that future contributors, including myself, may need to know about that feature, especially its implementation.

Missing documentation is technical debt, and I have near zero technical debt.

Of course, you may take issue with my assertion that I have zero or near zero technical debt, but this is basically true.

The reason is that I'm a perfectionist with my code. When I go to make changes, I would struggle if I did not fix any imperfections I see. For an example, see [3]. So I do actually remove everything I see before getting to coding.

This can be annoying and discouraging at times; I have phases where I stop programming for a few days or a week because I'm not looking forward to a refactor that I need to do, but I can't continue without it because I'm a perfectionist. But it does mean that I have very little technical debt.

It turns out that a lack of technical debt is a superpower; it makes features easy to add and keeps velocity high. So once I'm done struggling through removing the technical debt, it's great!

This process is special, and it really can only work for someone that is as much of a perfectionist as me, and if someone does, they will have to be used to not implementing as many features because while velocity is high, I still spend copious amounts of time on refactoring and on tests, although that does increase velocity too by making it easy to do large refactors without fear.

This process also means that I cannot work with other people; I actually can't keep a job in the industry. So don't adopt this process if you do work in industry.

[1]: https://gavinhoward.com/uploads/process.md

[2]: http://antirez.com/news/124

[3]: https://git.yzena.com/gavin/bc/commit/eb0cd870b91f3ad2a834f2...