Thursday, May 2, 2013

Things You Don't Need

It's hard to get rid of things, even harder to not acquire them in the first place. Perhaps that's because the new thing can only be seen as a positive change - its acquirer blind to any down sides. Dependencies form in strange ways. Even if it's probably bad for you and everything around you, it still manages to root itself into the new environment. You almost have to kill it to get rid of it. It's like you need the thing you don't need in order to realize you don't need it. Maybe that's why the wealthy are generally unhappy? They have the thing, realize they don't need it, and yet, cannot get rid of it. They probably can get rid of it, actually, they just choose not to for one reason or another, and it's also a hard decision to make in the first place. Why get rid of something when it can be kept around? We must have acquired it for some reason or another, right? To enhance our existence. To fix an already existing problem we've been having with something else we've acquired? Could be anything, really, we tend not to keep track of this stuff do we? Maybe we should. Maybe paying conscious attention to what we need, and what we do not need is an essential daily exercise.

The minimum requirements of a software application are those that satisfy the stakeholders. Wait, but what about when the stakeholder is misinformed about what they actually want. The software development process should include the part where those responsible for creating the software walk the stakeholder through the process of actually writing the code. Why this won't work, why that is cool. Programmers can actually be interested in the business domain if you let them. And they're probably your best tool for trimming the fat — identifying what you need versus what you don't. Too often, software is built on a hand-me-down basis. This is what I need, now go build it. There is no engagement. No back and forth. No light-bulb moments whereby crucial facts reveal themselves. In the spirit of iterative development, this is necessary. Your software is nothing but code, so why not involve the people who know it best — the people writing it. It takes time, to coach and familiarize developers with the problem and what users will be expecting. This activity alone, walking the developers through every minute detail of the problem domain might be enough to poke holes in the grand idea. It's best you do that sooner rather than later. Just talking out loud, trying to explain the idea, perhaps from the perspective of the customer, might be enough to push the software in another direction. Most importantly, identifying the bare essentials.

I mentioned that this is an iterative process, this communication between the stakeholders and builders. When you think you've identified the bare essentials, you haven't. Go back, do it again, and remove something else. Relentlessly hack the requirement meat off the bone, because that is how you reveal the skeleton application. The thing that most matters, the essentials that carry the thing forward. It's a messy, time-consuming job. Each iteration will probably involve writing code. Scratch that. It will involve writing code because you can't always envision dead weight. You have to implement it first. Then kill it. And therein lies the challenge — identifying the bare essentials, so as to avoid implementing cruft. Begin with the experiments. Right away. Think about any software system you've implemented in the past. Before any code was written, did you envision any of your components as junk, getting in the way of the real software, the foundation? Seeing is believing.

Based on the above observations, then, perhaps what we ought to be looking for aren't the bare essentials, but out-of-the-box software spam. Yes, that's right software spam — crap that burns programmers eyes as soon as they open their editor. What's the proper term, anti-patterns? Maybe. But those may be a little too generic because we're trying to build software, not a beautiful design that looks good on paper. What we need is sound knowledge of the programming environment. If the application is written in Java, we know what not to do (not write it at all?). If it's written in Python, we know what not to do. These little anecdotal tidbits are what make great programmers great. They know what sucks because they've implemented suck in the past. Nobody needs suck. And nobody sets out trying to implement suck, it just happens as a result of unique constraints around the project and general circumstance. But the the end result of implementing a monstrosity is that it is hard-wired in your brain moving forward. For the rest of your career, when you see that same monster poking its head around the corner, you'll recognize it right away. Past experiences of coding gone bad are necessary. A programming team will have tons of these past experiences, and thus, they're unlikely to happen in the current project.

But the past isn't the current project. The past was some other stakeholder, with potentially similar but nonetheless different problems to solve. Maybe you really do need the thing that was irrelevant before now. Maybe, but probably not. Still, adjusting for the current problems at hand is something that cannot be swept under the table. Programmers tend to stick to their beliefs once formed, and that can't happen either. Identifying cruft is a team effort. What was once garbage could now be the key to solving a major implementation headache. The biggest thing that has probably changed though is the fact that you're now dealing with a different set of stakeholders who may have a completely different set of personalities than your previous group. There is a balance, between doing things the right way, and how the stakeholders perceive those actions as a positive thing. It's simple, actually, this communication between developer and stakeholder when you really think about it. Be honest. Don't sugarcoat the fact that what they're trying to do needs to change. If that really isn't an option, and the stakeholders are unbending, it's time to start thinking about rewriting your software.

Why, exactly, would a user be unwilling to change the way the software works? Unwilling to lose a feature or two? If it prevents them from performing their everyday activities, that is one problem. If it radically re-arranges the user interface so as to confuse them to a breaking point, that doesn't accomplish anything good either. In most cases, software organizations can afford to lose the two users who complain for the sake of complaining. Especially in the interest of advancing maintainable code. But, in the real world, semi-successful software projects have evolved to live in a complex environment where they meet the needs of many. Where they solve lots of smaller, but related problems. They have a lot of moving parts that aren't easily removed, in other words. They work. They users are happy. But under the hood, the source files are screaming in agony. They need help. So although the you've met your goal and presented a cohesive software package to your users, the developers responsible for that code see things differently. There is a lot of cruft. The band-aids need to be replaced with stitches so the war wounds can heal naturally. But this is a near impossibility to accomplish in a large-scale, monolithic system. Time to rewrite, partition, and address the cross-cutting concerns of the users, and more importantly, creating a maintainable code base.

Forget about the requirements and user expectations for a moment. Think instead about the quality attributes. How reliable is each individual component? How fast is it? How much time needs to be spent on ensuring these qualities of the system are met? These are yet more dependencies that we may or may not need. They're often not needed. And this is especially true when it comes to added dependencies that will supposedly make these quality attributes a reality. But is it really so? Do you really need to rewrite a bunch of code to drop in a replacement piece that is twenty-five times faster than the code you already had in place? No, probably not. It's nice to be able to — fun even. Dependencies hurt, and you can do without them. Your reliance on a third-party component isn't a good thing. You don't need it unless it solves a fundamental problem and writes thousands of lines of boilerplate code for you. In reality, how many components do that? Really, what you're after is just one big dependency, and maybe a few smaller ones. But it's the smaller dependencies that can bite because they leave the impression that they're interchangeable. Well, if that's the case, interchange it. Right now, with your own code. You'll be better off in the long run because dependencies get in the way of the inevitable change down the road. The big dependency, like the web framework, isn't something that will change often, if ever, and so make that the focal point in how you decide to write components. Bend the code to your needs. You can, because it's your code.

Users may have their own two cents about what third-party components should be pulled in as a dependency. It is after all, their software. The rationale there, I suppose, is that they want it to perform a certain action, or to perform an action already implemented a certain way. Should be easy, right? This other project does it, and they do it using foo library. There you have it, you've gotten yourself reeled into a new dependency that you really didn't need. These ones are hard to deny, of course, when they come at the user's request, but successful software has to be a cooperative effort. Education in both directions. Developers explain what they don't need. Users and other stakeholders justify the existence of the software.

It's not as though most people involved in a software project don't actually know what they don't need. These people are smart, they've been there before, and they can smell superfluousness for what it really is. The additional component that isn't actually required, the one that ultimately becomes harmful down the road, doesn't make it's way in easily. But it does. How? Maybe that ought to be the focus — not letting these disastrous little buggers in in the first place. But thinking too deeply on such a subject, I'm afraid, will not lead to functional software. I'm convinced that you have to build the project sub-optimally first. Not ideal, but functional. Even this is hard to do, and when we finally get there, all the promises we made in the past no longer seem important. The cleanup effort takes on a new perspective — it seems like only risk as opposed to gain over the long run. But once again, it goes beyond that. Removing cruft is about deep understanding. Understanding about what the software does, and who it best serves. To best serve an individual, we must think about how it makes their lives easier. Huh? Yeah, sounds a little ridiculous, but it's true. My knee-jerk reaction is to better serve the organization this user works for, or to simply generalize the class of user and make sure that's who I'm aiming to please. Users are people, and we're improving their existence by presenting them with less crap. They'll understand. They'll get it the first time they use the leaner version of the product. It's the folks on your team that need to wrap their heads around the idea.

It really is all about mindset, at the end of the day. Software products compete on usefulness, and yet, this is something not broadly considered during the development of these systems. Software today is all about check-box features — the kind of thing no one really cares about aside from marketers. The real thing we lack isn't discriminatory know-how - we evaluate the necessity of any given component every time we use it. Well, when we come across it in code at least. However, that is where the evaluation of the things usefulness ends. Unless there is a bug, we're not thinking about how we can slim down what's working. It's on to the new thing, constantly growing the system, putting it in a perpetual cycle where there is simply no opportunity to remove the things we don't need. We need this opportunity, we need to scale back and scale down the design of our software. Engineering for simplicity will enable the larger things you want to accomplish with the software because you can compose systems out of smaller ones. Using only sub-systems you actually need.