Thursday, March 11, 2021

Quality Categories

 A big problem with software development is that people have trouble understanding the quality of the work and the consequences of rushing through it. 

Most people think that except for the interface, the rest of it, which is usually the bulk of it, is invisible. That’s not really the case, while it’s not easy to scroll through the code, configuration, and data in order to inspect them, if they are badly done, they set off plenty of long-term problems. Careful tracking of bugs and stability issues always points back to these types of problems, but because of the time lag, people don’t often make the correct associations.  


We need a set of defined categories to list out software quality. This is pretty good:


0. Busted

1. Demo

2. Barely Usable, but Hideous

3. Tolerable

4. Industrial Strength

5. Excellent

0. Busted

  • Not enough of it works to do anything practical

  • Maybe it doesn’t even build correctly

  • It is crashing for weird unknown reasons

  • Code is a mess

1. Demo

  • Kinda works

  • Looks like something that could be moved forward to being usable

  • Database is a mess

2. Barely Usable, But Hideous

  • Most functionality works

  • Navigation or workflow is awkward

  • Either the interface is really ugly (hideous externally)

  • Or the code is just a huge sloppy mess (hideous internally)

  • Bad data in the database

3. Tolerable

  • The functionality works, but isn’t elegant

  • The screens don’t induce violent seizures

  • The code is mostly formatted

  • There is some architectural structure to the code

  • It’s mostly consistent, not too much redundancy

  • The data is mostly clean

4. Industrial Strength

  • The screens are usable

  • The code is neat and tidy

  • The performance is okay

  • The dependencies are mostly up-to-date

  • There is an ongoing process to clean the database

5. Excellent

  • The interface is a work of art

  • There is no redundant code, anywhere

  • Everything is consistent

  • The code flows nicely, it is easy to extend

Sunday, February 21, 2021

Knowing How Things Work

 There are some real benefits in knowing how things work.

Jumping into something totally blind sometimes is fine, but only if you are there to learn it, or to enjoy it or, or just to take in the experience. But it’s always amateur-hour, the results might work out okay, but it is more likely they will leave some room to be desired.


Having a shallow overview might be okay sometimes, however being able to get right down to the details and see what is happening underneath is far, far better.


Most things that are complex are somewhat opaque from the outside. Their internal complexity can be counter-intuitive and built up over long and fractious histories. You need to know most of the parts, but it also helps to see how it all evolved, so that the exceptions make sense and fit back into the bigger picture.


There is no such thing as too much depth. There might however be too many details for any one person to cope with, so for very large and complex things, it is often necessary to specialize. A generalist can lead a bunch of specialists, but only if they are trusted and their advice heeded.  Thus one needs to know their limits: how much they know, how much they don’t know, and all of the little things that they think they might know, but really don’t. They need to know if they need someone else who knows more.


If you know how things work, you can make changes to them that have a much higher degree of continuing to work or actually improving stuff. If you don’t know how things work, then any changes you make will effectively cause random side-effects that you constantly are reacting to. That is terribly inefficient, somewhat dangerous, and there is no way of knowing once you start this, whether you’ll ever get it finished in a manner that is acceptable. 


Having an opinion about how things ‘should’ work is not the same as knowing how they actually work. And worse, opinions based on misunderstandings are more likely to be destructive, than helpful.


If you know how things work, then you know all of the changes that have to be made to achieve a certain goal. You also know how long it will take to make those changes. If it’s long-running work and you set a plan, then that plan will work out, in so long as there have not been any unforeseeable events. If the plan fails, it fails because of the things you didn’t know. Thus being able to successfully plan is a good way to prove that you actually know how things work.


It takes a lot of time to know how things work. It takes a lot of reading, and ingesting what you have learned, and then getting out there and experiencing stuff before you can really see how all of the pieces fit together. Learning from experienced mentors is the fastest way to get gain understanding. Learning from courses or textbooks is better at providing an overall view. Somethings can be known discretely, and that knowledge is definitive. Somethings can only be known as a sort of intuitive feel for how they will react in underlying difficult circumstances. You might know all of the parts really well, but are still not be entirely sure how they will react to a multi-dimensional set of changes. Most things require some form of balance, which is why oversimplifications tend to throw things out of whack. They favor one aspect of the problem over the others. 


Full knowledge is obviously the fastest and most effective way forward, but when that spans multiple people the team dynamics became an actual part of the thing getting changed, they are no longer separatable. Partial knowledge can be okay if it is accepted and account for, but you still have to find out about the unknowns and the unknown unknowns as well. 


A professional is someone who knows how things work for a given industry and can successfully reach any ‘possible’ goals. But they also know which goals are not possible, and which ones are unlikely. They know how to avoid failure.

Sunday, February 14, 2021

Gyrations

It’s instructive to walk through code sometimes. It reveals a lot more than just scanning it at a high level.

One of the things you find is that the code might take some pretty odd paths through the logic, just to get back to some really simple data changes.


These gyrations are of course unnecessary. They usually exist because the programmer didn’t know how to go from A to B. Instead, they went skewing off to H, bounced over to M, and then came back to B.


Sometimes this is a result of a second programmer coming into the code, with the intent to ‘just fix’ the minimum necessary. 


Sometimes it is a result of the original programmer not fully understanding the work they are doing. For some coders, they have a discreet set of ‘instructions’ that produce certain types of output. They assemble their code from this set. If this set has gaps, their code also has gaps, so they fill them by jumping over to other areas that they have in their set, doing intermediately work, then eventually jumping back. That is, they know how to go from H to M, and they know how to go from A to H, and from M to B. This can be recursive and often quite intertwined, there can be some pretty crazy gyrations going on.


It’s far easier if you want to code A to B, to just code A to B, even if there aren’t any underlying libraries that help. Most of these transformations are pretty trivial, they involve basic data type changes or structural rearrangements. If it turns out that it is not possible for scheduling reasons to do the code yourself, then the best way to handle this is to craft a function to encapsulate A to B. In that function, you can throw H and M into the mix, that’s okay. Later, if another programmer sees this and knows how to be more straightforward, it’s pretty safe for them to just replace the H and M madness with better code. If it’s encapsulated, then the impact of this change is easy to figure out. 


Sometimes programmers don’t want to encapsulate A to B because they feel that it is either hard to read or slow in performance. Neither issue really applies. It’s easier to read code if the weirdness is pulled away from it. You get a bigger sense of what the logic is trying to accomplish. The performance costs of adding lots of functions have been effectively trivial for decades. There are certain types of high-performance code where it might matter, but chances are that is not the code you are writing right now, and even if it was, it’s better to also provide a slower, more stable, more debuggable version in the source as well.


The biggest problem with large systems is that any and all disorganization feeds into making it a tangled mass of spaghetti. This always starts with little stuff, and each problem itself does not seem significant, but as these problems stack on top of each other, the entire edifice grows shaky and difficult. In a sense, passing through H and M is entirely artificial. They weren’t really needed, they are wasting resources, and they are just confusing the code. The code needs to get B from A, and it needs to do that in the clearest and cleanest way possible. Even if you are rushed, encapsulating that in a little function doesn’t take more than a few minutes, and it will pay off immensely later. 


Fixing this is actually really easy. Just start going through the big functions, creating littler ones. Once you have little ones, revise them to fit into other places in the code. Sometimes you have to unwind it a little to get it cleaned up. So, if A->H->M->B is intertwined with A’->Q->X->N->C, you probably need to separate them first, before you can encapsulate them. Obviously, it was less work to get it right originally, but it’s not complicated work to sit there and unravel it either. It’s just slow and boring. You have to do it a little at a time, then check that the code still runs correctly, then continue on. It definitely is work worth doing, particularly if there are still big changes to the system that are coming. Ignoring it only makes it worse.