A rant on the simple, yet under-appreciated art of cross-checking
I keep running to a problem online and I'm unsure that I'm explaining myself well.
Today I ran across a programming question on reddit: "What are some good examples of projects/modules/folder-structures to use with F#?"
To which I asked "What is it you want to do?" You want to learn the language? You want to learn an API/framework? You want to grow a startup? What is your goal?
This seemed to be the wrong question. The guy said he already knew how to program in F#. He just wanted "good" project structures.
I did not seem to be making progress. So, as I am wont to do, I wrote a thousand-word-ish reply. I'm not sure that was any better.
Allow me to generalize.
Masters solve problems using tools. They start with the problem, then they play around with possible solutions, finally, given the best solution they have, they pick the right tools and use them in the right way to get the job done. Without an understanding of the exact problem and amount of tooling and complexity required, they don't pick up the tools. The problem forces the tools. A->B
TDD works because it forces programmers to figure out what the code does before they make the code do it. Same goes for ATDD. However we tend to work backwards, starting with some picture-perfect solution and then trying to find out what the problem is.
Cross-checking solves this. Cross-checking is simply asking yourself, perhaps in code, what the minimal solution looks like. Then, no matter what happens later, you know how much work you could have done to get the same result. To be silly, if you want to multiply two numbers, your first step shouldn't be setting up a website. Most failed projects are just some more-obscure (and sad) version of that joke.
Cross-checking isn't just for coding. Want to streamline your process? Start from zero and create a team that gets the job done. It doesn't matter if later on you can use exactly what they're doing or not. It matters that now you have a cross-check on the amount of actual complexity in your process that's required to meet you enterprise goals.
We do so many things backwards. We start with constraints, supplementals, non-functionals, opinions, hype, and all sorts of other things that make us feel good. After we stick all of that into our work, we then start trying to figure out what the problem is and how to solve it.
The more stuff we stick on top, the less likely this is to work, no matter how good all of that stuff is. And just like fish don't know about water, we rarely are cognizant of just how much cruft we're carrying around. Cross-checking fixes that blindness.
When I see people struggle with programming, portfolio management, governance, etc, I see people who can't seem to get both the problem and their desired answer into their head at the same time. This is because, whether they know it or not, they're working backwards. People want to talk about chess pieces and how each moves instead of how to play chess, and playing chess is where the actual work is. So many people in tech are not working. They're busy, sure. Many of them may be miserable, some happy. But three years from now there'll be the same people, or people just like them, going through the same motions. No progress is made.
Cross-checking. Although my conversation involved F#, the principle is universal. What does the function do, ie, what exact problem are you solving --- regardless of anything else in the universe. Can you express the solution using a minimal amount of complexity? Great! Now you have a cross-check to use as you go along solving problems however you usually like to solve them.
To generalize still further without losing any veracity, suppose you are posed a question about structure. "Is this bridge a good design", "How do we organize a PMO", or "What's a good SCM for a ten-programmer project", "Is this a good database design". To answer any question about structure quality, you have to know what it's supposed to do and what constraints are in place as part of the solution. A bridge might need to allow trains to pass from one riverbank to another. The laws of physics and the cost controls in place are the constraints. A database might need to store accounting information for an enterprise and the constraints are fifth-normal form and a fast response time.
Cross-checking removes the constraints, which we are allowed to do in process and programming land, and just asks about the problem to be solved. Then and only then are we in a position to make qualitative decisions about the rest of it.
As an enterprise, either cross-check or accrete cruft and die. Your choice. As a programmer, it works the same way. It hurts more.