Mike Hadlow, a software developer, writes about software design, architecture anti-patterns and development practice.
HN commenters link his posts as classic descriptions of recurring problems, such as the configuration complexity clock and the lava layer anti-pattern.
I've done a few (started my career doing VB6 -> .NET porting). Working Effectively with Legacy Code was the first book I read. Email's in my profile
Team just 'finished' a migration of a 10 year old codebase from Java -> Kotlin (ended up with 100k SLOC). Part-time this took around 1.5 years, which was faster than I expected. Seeing benefits already but it'll take years to see if it was worth the opportunity cost. Also in the middle of a second migration of some aspects of the codebase to Rust/Svelte. Massive and permanent immediate benefits when this goes through.
- Have well-defined rationale, scope, and buy-in.
- Determine how you'll approach bad code that nobody's touched for a long time
- Testing guideline: ensure you 'could' test the code in the 'before' (typically about 15% coverage). If you can't test the 'after', you've probably picked the wrong language
- CI is table-stakes.
- Define the level of interop between old and new code that you want.
- If you can avoid a 'big bang', do so. The burden of a 'big test' beforehand and 'everything is now unstable' afterwards isn't an easy situation to solve (still working on this). Nobody wants to be in the position of 'you did nothing and everything's worse'
- Ensure you keep git blame working between old and new. git bisect is an ideal
- Set up linting immediately for the new project
- Optional: I went down the route of adding AST-based linting for a couple common errors/issues. Tempted to add some more. Didn't get…
> The problem with that is making a configuration engine is a lot less efficient than just writing the code to do the damn thing.
http://mikehadlow.blogspot.com/2012/05/configuration-complexity-clock.html
It took me a solid decade to accept these lessons. I cannot build a product that even remotely competes with things like Visual Studio and .NET tooling. How many hours would it take a solo developer to recreate a debugger experience that anyone else could be bothered to look at for more than 5 seconds?
The hardest part of building something configurable is the UI/UX. The moment you step outside of the host language, your tooling support goes to zero. Sure, you can pick other popular languages as the language of configuration (SQL, python, Lua, etc.) and piggyback off those ecosystems. But, you still have to build a way for a "non programmer" to visit all of the patchwork sites of "configuration" in a coherent way. Telling the business people to go edit a folder full of .py and .sql files will probably not be received well. Wrapping all of this with a fancy web interface doesn't make the problem less severe either (it makes it substantially worse).
If you have a way to build/deploy your product in <60 seconds from source, worrying about elaborate configuration and customization architecture seems extremely idiotic to me now. The best path I've found is something approximating a common library and a per-customer executable that consumes it and implements the expected…
> the solution shouldn't be a smart algorithm to enable further mis-use of excel/spreadsheeting-tools, but rather to migrate the processes to a database
In an ideal world only. Being able to have intricate computation definition being user-modifiable is a big feature. I have successfully 'appified' such Excel sheets in the past, only to be recognized as the new business rule maintenance guy. I no longer do that anymore: I identify moving parts, and these stay in the Excel world instead of going into the DB or a config file. I do clean the inputs and harden access control though.
The apps are quite often low traffic so this setup might cost at most 100 USD/y in wasted performance per app. Nobody tracks that anyway. The business guys are happy to maintain the thing, I'm happy they're not bothering me. What is not so good is the latency of some queries.
IMHO Excel is a great global minimum in the 'Configuration Complexity Clock' [1]. Or maybe I just don't care enough anymore.
[1] http://mikehadlow.blogspot.com/2012/05/configuration-complexity-clock.html
> And contrary to many people, I'm fine with the (eternal?) limbo that we then end up with: parts of the codebase using the new stuff, parts still using the old stuff.
That is fine when, as you described it, there are only two types of code: one still using old technique/technology A and the other using newer version B.
Where it becomes a problem is when you haven't finished the transition and realise you need to start moving on to C, or you leave and the new person doesn't understand the difference (and probably also has their own desired replacement). Before you know it, you have three or even more styles of code in play at once, with diminishing ability to keep moving to the latest version. This is sometimes called the lava layer antipattern [1]
[1] https://mikehadlow.blogspot.com/2014/12/the-lava-layer-anti-pattern.html?m=1
The difference is that I am not proposing a Turing complete config language, but rather proposing to build configurations with plain old JavaScript (or whatever language you want). I think a good read about this thinking is by Martin Fowler on Language Oriented Programming [0], specially the section about internal DSLs.
Another interesting read is around the configuration complexity clock [1], in that over time we move from hard coding things to building configurations to coming full circle and hard coding things again. I like to think internal DSLs closes that loop well.
[0] https://www.martinfowler.com/articles/languageWorkbench.html
[1] http://mikehadlow.blogspot.com/2012/05/configuration-complexity-clock.html
No mention of The Configuration Complexity Clock [1] which I always think deserves a link, but credit to the author for actually keeping it slim and readable over (IMHO) most of the newer additions to the landscape which either declare themselves as 'obvious' and aren't or just add features such as Pkl's extends keyword, moving us further round the complexity clock.
Of course just adding multiline strings is the start of the rabbit hole, now you need to think about leading line breaks, trailing line breaks, intermediate line breaks, whitespace chomping, and- oh heavens I've reinvented YAML, I think I need to lie down.
[1] https://mikehadlow.blogspot.com/2012/05/configuration-complexity-clock.html