Saturday, 11 May 2013

Fixation and variability

While explaining Why Everyone (Eventually) Hates (or Leaves) Maven, Neal Ford divides extensible software into two categories: composable and contextual.

Contextual software is best exemplified by plugin architectures - new features are added by slotting code into explicitly provided extension points. Composable software is best exemplified by the Unix ecosystem - new features are added by wiring together a combination of existing and custom components.

So what is it about contextual systems that provokes such hatred?

The key problem when making software extensible is where to put the variability. I have a fixed set of core features, but I also want others to contribute extra features that vary depending on their needs. How do I bind the fixed and variable functionality together so that they form a coherent system?

Maven accommodates variability like a classic contextual system. The promise (and the curse) of Maven is the consistency of its build lifecycle. There is a place for everything, and everything must be in its place. Maven affords very little flexibility on the relationship between variable additions and its fixed feature set.

Rake, on the other hand, empowers developers by allowing extension via a general purpose programming language. Rake qualifies as a composable system because any means of adding new functionality that can be expressed in Ruby can be used to extend it.

In composable systems, orchestration is variable. In contextual systems, orchestration is fixed. Keeping the means of combination in userland is an additional burden on those who would extend your system, but it's more than offset by the flexibility and power they enjoy in the long term.

Your software will grow, but your relationship with Maven won't. If When that happens, it's time to leave.


Sunday, 16 December 2012

Dickens on Ruby on Rails

There is much good in it; there are many good and true people in it; it has its appointed place. But the evil of it is that it is a world wrapped up in too much jeweller's cotton and fine wool, and cannot hear the rushing of the larger worlds, and cannot see them as they circle round the sun. It is a deadened world, and its growth is sometimes unhealthy for want of air.

- Charles Dickens, Bleak House

Friday, 17 August 2012

It's all production

Every now and then, I come across a discussion about whether test code deserves as much care as production code. I understand what is meant (the answer is yes), but I don't accept the premise of the question.

All code is production code. Deployment scripts, tests, database migrations and Java controllers all play a part in delivering value to the users. If a piece of code really has no effect on production, why was it ever written?

Wednesday, 20 June 2012

Einstein on Clojure

It can scarcely be denied that the supreme goal of all theory is to make the irreducible basic elements as simple and as few as possible without having to surrender the adequate representation of a single datum of experience.

- Albert Einstein, The Herbert Spencer Lecture, delivered at Oxford (10 June 1933)

Saturday, 16 June 2012

It's time

EuroClojure 2012 was almost a month ago. Now that my thoughts have settled I've made an attempt to distill what the event meant to me.

First of all, I was impressed at the intellectual engagement of the speakers and attendees. This is a grown-up community. Talks were less about "How do I do X in Clojure" and more about using the language to move the industry forward.

If you're a developer who's interested in the avant-garde, you should be paying attention to what this community is coming up with. I left feeling that Clojure is less a programming language and more a delivery mechanism for the Clojure philosophy.

But what is the Clojure philosophy?

Whether it was Rich Hickey railing against in-place database updates, Nick Rothwell animating art, Max Weber explaining CQRS or Paul Ingles exploring event-sourced analytics, an underlying theme was that time deserves first-class representation in interesting systems.

Even more directly, Bruce Durling's Incanter incantations and Jeff Rose and Sam Aaron's waveform wizardry manipulated time-series data before our eyes to great effect.

In an age where we can no longer assume that the universe marches to the beat of a single CPU, implicit time-management is no longer a convenience but a crippling limitation.

So when someone asks me "Should I adopt Clojure yet? What is the killer application?" I will answer them:

It's time.

Sunday, 10 June 2012

Unthreading

Multi-threading is hard. To fully understand a piece of multi-threaded code the reader must mentally interleave two or more competing timelines. Our poor primate brains have enough trouble managing one thread of execution.

But what if we didn't have to carry around the idea of time at all? What if we could move about the codebase without having to mentally pause, play, fast-forward and rewind a hypothetical execution instance of the program?

The venerable and venerated Structure and Interpretation of Computer Programs comments with sadness on the entrance of the mutation-snake into the Lisp Garden of Eden: 
With objects, we must be concerned with how a computational object can change and yet maintain its identity. This will force us to abandon our old substitution model of computation in favor of a more mechanistic but less theoretically tractable environment model of computation. The difficulties of dealing with objects, change, and identity are a fundamental consequence of the need to grapple with time in our computational models. These difficulties become even greater when we allow the possibility of concurrent execution of programs.
Immutability makes code easier to understand because it allows the reader to use a simpler mental model.

Code that exclusively employs immutable (immortal) variables is effectively unthreaded from the reader's point of view. The meaning of the code is no longer dependent on time at all. The value of a symbol is a function of its definition - history is irrelevant.

Single-threaded code is easier to understand than multi-threaded code. But unthreaded code is best of all.

Friday, 25 November 2011

Overlapping magisteria

I've written that unit tests and acceptance tests represent two non-overlapping magisteria. I've realised I need to revise this.

Unit tests and acceptance tests specify internal and external behaviour respectively. There is no inherent reason why external behaviour should be trivially translatable to internal behaviour, because the structure of the internals need not bear any resemblance to the model the system presents to the world.

However, that is only true of systems in general. Systems produced using a domain-driven design methodology deliberately enforce structural similarity between the system's implementation and the domain it represents.

Because of this, it's possible to make inferences between external and internal properties of the system. For example, if a
LoginFailure
object returns
true
to the method
shouldLockAccount()
when its
int
field called
previousAttempts
is
2
or greater, there's a good chance that if a user thrice enters the wrong password that their account will be disabled.

If we know something about a well-designed system's external behaviour, we can generally extrapolate about its internal behaviour (and vice-versa).