When we get a complex problem to solve, we are taught to decompose that into pieces. But often that is not enough to get something sophisticated to work correctly.
I’ll start with some simplified goals. Usually the most common “special case”.
I lay out some groundwork. Bottom-up programming is always the strongest approach, but it still needs to be guided. So you can whack out all of the common foundational parts you know about, but that isn’t wide enough to get it all completed. So don’t. Just enough to get going.
Then take that special case and work from the top down.
If it’s a GUI, you start with a common screen like the landing one. If it’s an ETL, you just get some relatively close test data. If it's a calculation, you put in a simplish test case. Ultimately you need a fairly simple starting point.
Then you wire it up. Top to bottom. While wiring, you will find all sorts of deficiencies, but you constrain yourself to only fixing the core problems. But you also note all of the others, do not forget them, and dump them into a big and growing list of stuff to do. They are important too, just not now.
So, hopefully, now you have something crude that kinda solves the issue in very limited circumstances and a rather tiny foundation for it to sit on. It is what a friend of mine used to call an inverted T.
If it sort of works, this is not the end, it is just the beginning. You iterate now, slowly evolving the code into the full complex thing that you need.
One key trick is to pass over the unknown-unknowns as early as possible. If there is some technology you are unfamiliar with, or a difficult algorithm. You touch these areas with something crude and you do it as early as possible. Unknown-unknowns are notorious for turning out to be way larger than you have time or estimates to handle. They like to blow stuff up. The sooner you deal with them the more likely you can get a reasonable sense of when the work will be completed.
For each iteration, once you’ve decided what you need to do, you first need to set the stage for the work. That comes in the form of a non-destructive refactoring. That is, if your structure is crude or wonky, you rearrange that first in a way that does not change the overall behavior of the code. If you don’t have enough reuse in the code, you fix that first.
If you did a good job with the non-destructive refactoring, the extension code should be easy, It is just work now. Sure, it takes time, but you spend the time to make sure it is neat, tidy, and super organized. You need to do this each and every time, or the iterations will converge on a mess. You don’t want that, so you avoid it early too.
Once you get into this habit, each and every development is a long-running series of iterations, and each iteration is cleaning up stuff and making it better and more capable. The code will keep getting a little better each time.
This has two core benefits. First, is that you can level out your cognitive effort. The non-destructive refactorings are boring, but they are always followed with the expansions. As you are tracking everything, you have a huge list of todo items, so there is never a ‘hurry up and wait’ scenario. If one little thing is stuck on other people, there are plenty of other better things to do anyway.
The second benefit is that all of the people around you see that the work is getting better and better. They may have been nervous initially that the total effort will be slow, but continual improvements will dissolve that. What they don’t want is big highs and lows, and an endless number of unknown-unknown dramas. That wonky approach will keep them from trusting you.
Done well, the development is smooth. There are plenty of little issues, but they do not derail the path forward. eventually, you will get to something really sophisticated, but you will not get lost in the complexity of it. You will not crumble under technical debt. This is when you know you have mastered your profession.
I’ll start with some simplified goals. Usually the most common “special case”.
I lay out some groundwork. Bottom-up programming is always the strongest approach, but it still needs to be guided. So you can whack out all of the common foundational parts you know about, but that isn’t wide enough to get it all completed. So don’t. Just enough to get going.
Then take that special case and work from the top down.
If it’s a GUI, you start with a common screen like the landing one. If it’s an ETL, you just get some relatively close test data. If it's a calculation, you put in a simplish test case. Ultimately you need a fairly simple starting point.
Then you wire it up. Top to bottom. While wiring, you will find all sorts of deficiencies, but you constrain yourself to only fixing the core problems. But you also note all of the others, do not forget them, and dump them into a big and growing list of stuff to do. They are important too, just not now.
So, hopefully, now you have something crude that kinda solves the issue in very limited circumstances and a rather tiny foundation for it to sit on. It is what a friend of mine used to call an inverted T.
If it sort of works, this is not the end, it is just the beginning. You iterate now, slowly evolving the code into the full complex thing that you need.
One key trick is to pass over the unknown-unknowns as early as possible. If there is some technology you are unfamiliar with, or a difficult algorithm. You touch these areas with something crude and you do it as early as possible. Unknown-unknowns are notorious for turning out to be way larger than you have time or estimates to handle. They like to blow stuff up. The sooner you deal with them the more likely you can get a reasonable sense of when the work will be completed.
For each iteration, once you’ve decided what you need to do, you first need to set the stage for the work. That comes in the form of a non-destructive refactoring. That is, if your structure is crude or wonky, you rearrange that first in a way that does not change the overall behavior of the code. If you don’t have enough reuse in the code, you fix that first.
If you did a good job with the non-destructive refactoring, the extension code should be easy, It is just work now. Sure, it takes time, but you spend the time to make sure it is neat, tidy, and super organized. You need to do this each and every time, or the iterations will converge on a mess. You don’t want that, so you avoid it early too.
Once you get into this habit, each and every development is a long-running series of iterations, and each iteration is cleaning up stuff and making it better and more capable. The code will keep getting a little better each time.
This has two core benefits. First, is that you can level out your cognitive effort. The non-destructive refactorings are boring, but they are always followed with the expansions. As you are tracking everything, you have a huge list of todo items, so there is never a ‘hurry up and wait’ scenario. If one little thing is stuck on other people, there are plenty of other better things to do anyway.
The second benefit is that all of the people around you see that the work is getting better and better. They may have been nervous initially that the total effort will be slow, but continual improvements will dissolve that. What they don’t want is big highs and lows, and an endless number of unknown-unknown dramas. That wonky approach will keep them from trusting you.
Done well, the development is smooth. There are plenty of little issues, but they do not derail the path forward. eventually, you will get to something really sophisticated, but you will not get lost in the complexity of it. You will not crumble under technical debt. This is when you know you have mastered your profession.
No comments:
Post a Comment
Thanks for the Feedback!