The ASmaP Principle (part 2)
To recount the gist of the previous blog: the ASmAP-principle strives to minimize the footprint of your project/application in terms of code base size, dependencies and configuration complexity. (In fact, it’s a particular re-statement of the KISS-principle.)
What’s the use?
The main reason to follow this principle is that projects/applications tend to grow fast to a point where it’s quite impossible to fit a good mental model of it into your brain in any reasonable amount of time. In turn, this inhibits you in becoming productive on those projects and making the right coding decisions. By constantly making an effort to reduce incidental complexity, you’ll make development on the project more effective and productive – not only in the long run, but also in the short run. You should see a decrease of time-to-market, bug count, maintenance costs after a short time, while hooking up extra devs to the project and effectively communicating with your customer becomes easier.
The ASmaP Principle is not about being as smart as possible in as few lines of code, it’s about obtaining/using/achieving the simplest possible solution, but not simpler – free after Albert Einstein. It also pertains to documentation: provide just enough text to clarify what your code is doing beyond what the code already clearly expresses on its own. This is also an incentive to write your code in a way which makes documentation largely unnecessary. I usually get away with some explanation about a class as a whole and some usage patterns.
Now, I’m not advocating premature optimization (of the performance of implemented functionality) here. I do think that it is the root of at least a lot of evil and any optimization effort should be based on profiling. Moreover, optimization is usually the source of a lot of incidental complexity since it doesn’t expand functionality as such only the performance of the implementation. As an example, consider caching: it requires adding a layer of indirection, thinking about invalidation and usually comes with a bunch of assumptions and runtime behaviors which are
Kill your dependencies
Dependencies on external libraries, frameworks, components, runtimes, environments and what-have-you -i.e., all this is within your direct sphere of influence but which you didn’t actually create yourself- are often the biggest source of incidental complexity. Each of these dependencies add their own dependencies but more importantly, they add to the information you need to carry around in your head: what’s the library’s API like, how to include the library correctly, how to instantiate the component, what are the peculiarities of this runtime, what do I need to configure in that environment to have it switch between dev/test/acc/prod?
Obviously, you can document much of this, but documentation has to be kept up-to-date and you’ll always need to remember a non-trivial amount of information to even be able to get things working at all, let alone productively.
Furthermore, dependencies tend to solve only half of a problem that technical rather than functional in nature – in other words: it’s falls squarely in the category of incidental complexity. My favorite example is Hibernate: it gives you (un-)marshalling of Java objects from/to a relational database, but at the price of having to write XML mapping files (well, in the pre-@annotation days, at least) which you need to keep in sync with both the Java classes and DB schema. Were I to generate the latter, I could just as well generate the necessary (un-)marshalling methods. (With annotations most of the meta data resides in the Java class, at least.)
To gauge whether you have too many dependencies in your Java project, I propose the following:
The Maven Rule: if your project uses Maven for dependency management, you have too many dependencies. Period.
While Maven undeniably makes the whole dependency management problem more bearable, it generally expands the same problem by making it just too friggin’ easy to drag in more dependencies without every really wondering whether that’s a good idea. Think about what you really need from dependency #37 that you put in your project. Some questions you could ask yourself:
- Is the same functionality already implemented (more-or-less equivalently) somewhere else in your project (typically using another dependency-of-ill-repute)? – If so, Refactor and remove all but the most-used dependency. Sometimes, you’ll find that you’re better off writing your own implementation without using external dependencies to snugly fit your needs.
- Does it solve your entire technical problem? If not, are better frameworks available?
- Does it provide an complete paradigm/way-of-thinking/-working (such as the Google Guice or Apache Wicket frameworks) or does it “only” provide some utility functions (such as Google Guava)? In the latter case, see the first point.
Note that I’m not advocating NIH here, but mind that there’s also no point in shoehorning a problem in a particular shape so that it happens to fit some “reusable” code: the fit is often quite bad and the primary code not very aesthetically pleasing. That’s a general problem with reuse (I have): all code makes a lot of implicit assumptions and unless these assumptions are met by your problem (domain) on a grand scale, things are not going to fly. The most rewarding uses of reusable components are often those where the component either matches the problem domain perfectly or is completely functionally orthogonal.
Not only can you reduce the sheer number of artifacts on which you’re dependent, you can also make sure that the number of direct dependencies (i.e., part X makes direct use of dependency Y by explicit reference such as an import) is as small as possible. This is analogous to your project architect insisting that application layers only interact in very controlled ways (and preferably through DTOs). Often, you can hide a large number of dependencies in a specific sub system which then governs use of and acces to its dependencies.