Showing posts with label architecture. Show all posts
Showing posts with label architecture. Show all posts

T-shaped designers

BBC 2 is running a series of documentaries called The Genius of Design. Programme 2 is "Design for Living" and discusses, amongst other things, the Bauhaus and its influence. It's getting on for a century since the hay-day of the Bauhaus and it's always worth being reminded of the influence it had—got any tubular steel furniture in your house or office? Bauhaus. Any lighting fixtures with push–on/push–off switches? Bauhaus. Got a fitted kitchen? Bauhaus.

The segment on the fitted kitchen was interesting. A fitted kitchen seems like a natural and obvious thing now, but the idea had to be invented. The discussion of the Frankfurt Kitchen in the programme was the start of an interesting thread. Users of the kitchen tended to be a bit ill-disciplined. Certainly the tended to disregard the labels permanently attached to the custom–made drawers and put any old thing in them. Users found that the kitchen was built to support well only certain workflows, workflows that they didn't like, didn't understand and couldn't change. Workflows devised by an architect who couldn't cook.

Meanwhile, another Bauhaus architect, Le Corbusier, is being given free reign to redesign entire cities, up to the point of making models, anyway. Filling them with great towers full of his “machines for living in”. And we know how that worked out once people started taking it seriously.

If you are a regular reader of this blog you probably know where I'm going next.

Commentators on software development often seem to exhibit a lot of discipline envy. Two common themes are that 1) our projects should exhibit the reliability of those in the “established” branches of engineering, and 2) our projects should exhibit the conceptual integrity attained by building architects.

That conceptual integrity can be a dangerous thing. Lihotzky's kitchens had a lot of conceptual integrity (and a lot of research to back that up), Corb's vision of mass housing (and its implementation by later architects) had a really astonishing amount of conceptual integrity. Neither leads to much in the way of joy for users (*). The Bauhaus architects designed a lot of chairs, none are comfortable to sit in.

One of the designers interviewed in the programme explained the problem along these lines: architects tend to be ‘I’ shaped, by which he means they have a very deep knowledge and skill in their craft, but not a lot else going on. Designers tend to be ‘T’ shaped, deep in craft but also with a breadth that touches many other disciplines. And from that breadth comes the ability to design objects that people can comfortably incorporate into their lives.

I think that the application of this thought to the software world is clear.

(*) The very few dense housing projects that Le Corbusier himself built have proven to be resilient and popular. It's the shoddy works inspired by his ideas and executed without his art that are the problem.

Learning from Architects

From dog–houses to skyscrapers, the discipline of the building architect has long been a rich source of metaphor for system architects. I don't disagree, indeed in one of my contributions to the 97 Things Every Software Architect Should Know I recommended that software architects should learn from architects of buildings. So, you can imagine that my eye was caught by Matthew Frederick's 101 Things I Learned in Architecture School. This small book contains the eponymous count of hints, tips and tricks from a qualified architect to architecture students.

This is a good resource for those of us who would learn from architects of buildings, as it is advice to would–practitioners from a practitioner and as such relates to what building architects actually do, rather than what someone who isn't a practitioner thinks they must, surely, do. This latter is a common failure mode of software professionals seeking inspiration from other disciplines—all the way back to the very first Software Engineering conferences of the late 1960's, in which an hallucinatory notion of what "engineers" do was foisted upon us. But I digress.

Some of the 101 are very low level and very specific (eg Number 90 "Roll your drawings for transport or storage with the image side facing out"), others are much broader and seem to me to have relevance for any sort of design work.

Number 15 tells us that "A parti is the central idea or concept of a building." Wikipedia tells us that parti is from the French prendre parti "to make a decision". The parti captures, presents and summarises the highest level decision that has been made about the organising principle of an entire building or building project, and examples are given where the parti expresses all that in one, highly abstract, diagram.

A parti sounds to me a lot like a system metaphor.

Number 100 tells us that the parti should have a name, such as "half–eaten donut" or "meeting of strangers". Could the nominal (or de facto [*]) architect of the system that you are working on draw such a diagram? Would it tell anyone anything if they did? Could they name the parti, the very highest level design decision from which the rest of the system design flows?

Number 28 tells us that a good designer isn't afraid to throw away a good idea. Notice: a good idea. An idea can be good and not fit with the parti, in which case it has no place in the design. We are advised to "save [...] good but ill–fitting ideas for another time and project—and with the knowledge that they might not work then, either." When was the last time you (or your team) threw out a good idea?

Number 46 tells us to "Create architectural richness through informed simplicity or an interaction of simples rather than through unnecessarily busy agglomerations". Frederick warns particularly against "busying up a project with doodads because it is boring without them; agglomerating many unrelated elements without concern for their unity because they are interesting in themselves." What interesting doodads does your current project have?

One reason to follow the guidance of Number 46 is given in Number 51, which observes that "Beauty is due more to the harmonious relationships among the elements of a composition than to the elements themselves". One achieves this beauty through a design process, or system.

Number 77 cautions that "No design system is or should be perfect. Designers are often hampered by a well–intentioned by erroneous belief that a good design solution is perfectly systematic [...] but nonconforming oddities can be enriching, humanising aspects of your project." 77 also observes that "exceptions to the rule are often more interesting that the rules themselves."

Number 81 notes that "Properly gaining control of the design process tends to feel like one is losing control of the design process" 81 advises that the designer should "accept uncertainty. Recognise as normal the feeling of lostness that attends to much of the process. Don't seek to relieve your anxiety by marrying yourself prematurely to a design solution; design divorces are never pretty" No, they never are.

Number 99 can help. It says "Just do something. [...] don't wait for clarity to arrive before beginning to draw. Drawing is not simply a way of depicting a design solution; it is itself a way of learning about the problem you are trying to solve." I think that much the same can be said for coding. Are the design procedures in your team aligned with these principles?

[*] Even the most self–organised, most cross–functional, most Agile, most collectively–code–owning software development team will have one individual who knows most about (and likely has most influence over) the architecture of the system. You can pretend otherwise, or you can take advantage of it.

97 Things Every Software Architect Should Know

The 97 things project is approaching a milestone: having 97 things which, in the opinion of one self–selecting group (which includes myself), every software architect should know. The intention is that the "best" 97 things go into a book to be published by O'Reilly, but that the forum will continue to be active, to gather new so–called "axioms" and discussion of same. 

What do you think every software architect should know?

TDD, Mocks and Design

Mike Feathers has posted an exploration of some ideas about and misconceptions of TDD. I wish that more people were familiar this story that he mentions:
John Nolan, the CTO of a startup named Connextra [...] gave his developers a challenge: write OO code with no getters. Whenever possible, tell another object to do something rather than ask. In the process of doing this, they noticed that their code became supple and easy to change.
That's right: no getters. Well, Steve Freeman was amongst those developers and the rest is history. Tim Mackinnon tells another part of the story. I think that there's actually a little bit missing from Michael's decription. I'll get to it at the end.


A World Without Getters

Suppose that we want to print a value that some object can provide. Rather than writing something like statement.append(account.getTransactions()) instead we would write something more like account.appendTransactionsTo(statement) We can test this easily by passing in a mocked statement that expects to have a call like append(transaction) made. Code written this way does turn out to be more flexible, easier to maintain and also, I submit, easier to read and understand. (Partly because) This style lends itself well to the use of Intention Revealing Names.

This is the real essence of TDD with Mocks. It happens to be true that we can use mocks to stub out databases or web services or what all else, but we shouldn't. Not doing that leads us to write code for each sub-domain within our application in terms of very narrow, very specific interfaces with other sub-domains and to write transducers that sit at the boundaries of those domains. This is a good thing. At the largest scale, with functional tests, it leads to hexagonal architecture. And that can apply equally well recursively down to the level of individual objects.

The next time someone tries to tell you that an application has a top and a bottom and a one-dimensional stack of layers in between like pancakes, try exploring with them the idea that what systems really have is an inside and a outside and a nest of layers like an onion. It works remarkable wonders.

If we've decided that we don't mock infrastructure, and we have these transducers at domain boundaries, then we write the tests in terms of the problem domain and get a good OO design. Nice.


The World We Actually Live In

Let's suppose that we work in a mainstream IT shop, doing in-house development. Chances are that someone will have decided (without thinking too hard about it) that the world of facts that our system works with will live in a relational database. It also means that someone (else) will have decided that there will be a object-relational mapping layer, based on the inference that since we are working in Java(C#) which is deemed by Sun(Microsoft) to be an object-oriented language then we are doing object-oriented programming. As we shall see, this inference is a little shaky.

Well, a popular approach to this is to introduce a Data Access Object as a facade onto wherever the data actually lives. The full-blown DAO pattern is a hefty old thing, but note the "transfer object" which the data source (inside the DAO) uses to pass values to and receive values from the business object that's using the DAO. These things are basically structs, their job is to carry a set of named values. And if the data source is hooked up to an RDBMS then they more-or-less represent a row in a table. And note that the business object is different from the transfer object. The write-up that I've linked to is pretty generic, but the inference seems to be invited that the business object is a big old thing with lots of logic inside it.

A lot of the mechanics of this are rolled up into nice frameworks and tools such as Hibernate. Now, don't get me wrong in what follows: Hibernate is great stuff. I do struggle a bit with how it tends to be used, though. Hibernate shunts data in and out of your system using transfer objects, which are (lets say) Java Beans festooned with getters and setters. That's fine. The trouble begins with the business objects.


Irresponsible and Out of Control

In this world another popular approach is, whether it's named as such or not, whether it's explicitly recognized or not, robustness analysis. A design found by robustness analysis (as I've seen it in the wild, which may well not be be what's intended, see comments on ICONIX) is built out of "controllers", big old lumps of logic, and "entities", bags of named values. (And a few other bits and bobs) Can you see where this is going? There are rules for robustness analysis and one of them is that entities are not allowed to interact directly, but a controller may have many entities that it uses together.

Can you imagine what the code inside the update method on the GenerateStatementController (along with its Statement and Account entities) might look like?
Hmmm.


Classy Behaviour

Whenever I've taught robustness analysis I've always contrasted it with Class Responsibility Collaboration, a superficially similar technique that produces radically different results. The lesson has been that RA-style controllers always, but always, hide valuable domain concepts.

It's seductively easy to bash in a controller for a use case and then bolt on a few passive entities that it can use without really considering the essence of the domain. What you end up with is the moral equivalent of stored procedures and tables. That's not necessarily wrong, and it's not even necessarily bad depending on the circumstances. But it is completely missing the point of the last thirty-odd years worth of advances in development technique. One almost might as well be building the system in PRO*C

Anyway, with CRC all of the objects we find are assumed to have the capability of knowing things and doing stuff. In RA we assume that objects either know stuff or do stuff. And how's a know-nothing stuff-doer get the information to carry out its work? Why, it uses a passive knower, an entity which (ta-daaah!) pops ready made out of a DAO in the form of a transfer object.

And actually that is bad.


Old Skool

Back in the day the masters of structured programming[pdf] worried a lot about various coupling modes that can occur between two components in a system. One of these is "Stamp Coupling". We are invited to think of the "stamp" or template from which instances of a struct are created. Stamp coupling is considered (in the structured design world) one of the least bad kinds of coupling. Some coupling is inevitable, or else your system won't work, so one would like to choose the least bad ones, and (as of 1997) stamp coupling was a recommended choice.

OK, so the thing about stamp coupling is that it implicitly couples together all the client modules of a struct. If one of them changes in a way that requires the shape of the struct to change then all the clients are impacted, even if they don't use the changed or new or deleted field. That actually doesn't sound so great, but if you're bashing out PL/1 it's probably about the best you can do. Stamp coupling is second best, with only "data" coupling as preferable: the direct passing of atomic values as arguments. Atomic data, eh? We'll come back to that.

However, the second worst kind of coupling that the gurus identified was "common coupling" What that originally meant was something like a COMMON block in Fortran, or global variables in C, or pretty much everything in a COBOL program: just a pile of values that all modules/processes/what have you can go an monkey with. Oops! Isn't that what a transfer object that comes straight out of a (single, system-wide) database ends up being? This is not looking so good now.

What about those atomic data values? What was meant back in the day was what we would now call native types: int, char, that sort of thing. The point being that these are safe because it's profoundly unlikely that some other application programmer is going to kybosh your programming effort by changing the layout of int.
And the trouble with structs is that they can. And the trouble with transfer objects covered in getters and setters is that they can, too. But what if there were none...


Putting Your Head in a Bag Doesn't Make you Hidden

David Parnas helped us all out a lot when in 1972 he made some comments[pdf] on the criteria to be used in decomposing systems into modules
Every module [...] is characterized by its knowledge of a design decision which it hides from all others. Its interface or definition was chosen to reveal as little as possible about its inner workings.
Unfortunately, this design principle of information hiding has become fatally confused with the implementation technique of encapsulation.

If the design of a class involves a member private int count then encapsulating that behind a getter public int getCount() hides nothing. When (not if) count gets renamed, changed to a big integer class, or whatever, all the client classes need to know about it.

I hope you can see that if we didn't have any getters on our objects then this whole story unwinds and a nasty set of design problems evaporate before our eyes.


What was the point of all that, again?

John's simple sounding request: write code with no getters (and thoroughly test it, quite important that bit) is a miraculously clever one. He is a clever guy, but even so that's good.

Eliminating getters leads developers down a route that we have known is beneficial for thirty years, without really paying much attention. And the idea has become embedded in the first really new development technique to come along for a long time: TDD. What we need to do now is make sure that mocking doesn't get blurred in the same was as a lot of these other ideas have been.

First step: stop talking about mocking out infrastructure, start talking about mocks as a design tool.