Scaffolding code as sign of maturity
One of the clearest signs of maturity that I’m looking for when reading code is the scaffolding that were used.
Just like in physical construction, it is often impossible to start by building everything properly from the get go, and you start by building temporary scaffolding to get you going. In some cases, those can be things that you need to actually build the software, but I have found that scaffolding are mostly useful in debugging and understanding issues.
For example, if I’m working on a complex data structure, it would be very useful to be able to dump it into a human readable format, so I can visually inspect it and understand how it works.
In the recent low level trie example, I have a file dedicated to doing just that, it contains some code to print the trie as well as validate the structure, and it was very useful to figure out certain things.
If the project is big enough, and mature enough, those scaffolding take on a permanent nature. In RavenDB, for example, they are debug endpoint and live visualizations that can help an administrator to figure out exactly what is going on with the system. The more complex the system, the more important the scaffolding become.
One very important consideration is what kind of scaffolding is being built. For example, if you throw a bunch pf printf all over the place while you are debugging, that is helpful, but that isn’t something that will remain over time, and in many cases, the second or third time that you find yourself having to add code to help you narrow down a problem, you want to make this sort of code a permeant part of your codebase.
One of the more complex pieces in building Voron was the B+Tree behavior, in particular when dealing with page splits and variable size data. We build a lot of code that would verify that structure and invariants for us, and it is running as part of our CI builds to ensure that stuff doesn’t sneak in.
One of the things that we teach our new hires is that one of the things that we need to have not just working code, but also all of the surrounding infrastructure. Everything that I need to work with, diagnose and manage the system in production over long periods of time. I distinctly remember a debugging session where we had to add a whole bunch of diagnostics code to figure out some really gnarly issue. I was pairing with another dev on that on his machine, and we were working on that for a long time. We finally managed to figure out what the problem was and fix that, and I left and got the final PR with the fix later in the day.
None of the diagnostics code was in it, and when I asked why the answer was: “We fixed the problem, and we didn’t need it, so I removed it.”
That is not the kind of thing that you remove, that is the kind of thing that you keep, because you can bet your bottom dollar that we’ll need it again, when the next problem shows up.
Comments
I think of scaffolding as something that is quick to stand up and tear down and is generally short lived. What you've described above, I would call infrastructure. It tends to be slow & expensive to build, is long lived, and takes time to recoup the investment. But when that time comes, it can pay big dividends.
Chris, The idea is that those kind of things are typically done temporarily (adding a few console write lines to figure out what is going on). But if you don't delete them after the fact, that gives a lot of benefits, and you need to do just a little bit more to make it a worthwhile feature.
I actually agree with your colleague here. If it isn't being used, then it gets deleted.
For me personally if you have to keep a few special methods around to assist in debugging a problem. Then I would question why that problem is recurring at the rate it is that debugging code has be to kept in source control?
JustPassingBy, A complex data structure that you are going to live with for a long time has many opportunities to break in interesting ways. Having debug code available (for example, to do extra validation when enabled) is very helpful. Not because you need it very often, but because when you need it, it is very useful to already be here.
It makes me question our usual definition of 'live code' vs. 'dead code'. Is it code that has some potential to be run, or code that is actually run 'in production'?
In some sense, maybe extracting this scaffolding code - fully separating it and encapsulating it outside the original code's context - lets it remain 'live' within its intended use cases, and helps separate concerns properly? That's understandably not always possible, but... food for thought. Hmm.
This reflects my experience. Debugging code is very usefull and a sign of mature code. It should be kept.
I´m also the opinion, that mature 24/7 code is "ugly" code, it has a lot of try/catch, logging, guarding, special case handling, retries,... up to 10%-30%.
When I see samples like "var client = new Weblient(); var response = client.UploadData(uri, bytes);" I know that this is a sample or the author did not test this under real cirumstances.
Where is the retry? What happens, if the connection gets lost - Where is the try catch? In german we call this "Nice weather code" it only works on nice weather when the sun shines, but what happens when a storm or thunder comes?
I think many developers in .NET world when they hear 'scaffolding' understand.. source code generation, as the one done for ASP.NET projects in Visual Studio (https://www.asp.net/visual-studio/overview/2013/aspnet-scaffolding-overview ), but indeed the terminology is more general.
Comment preview