Design bliki
AbundantMutation, AcademicRotation, AccessModifier, Agiledox, AltNetConf, AnemicDomainModel, Annotation, ApplicationBoundary, ApplicationDatabase, AssetCapture, BuildLanguage, BuildingArchitect, CallSuper, CatastrophicFailover, CheaperTalentHypothesis, ClassInstanceVariable, ClockWrapper, Closure, Closures, CobolInference, CodeSmell, CollectionClosureMethod, CommandOrientedInterface, CommandQuerySeparation, ConstructorInitialization, ContextualValidation, ContradictoryObservations, CourtesyImplementation, CurrencyAsValue, CustomerLoyaltySoftware, DataClump, DataModels, DatabaseStyles, DatabaseThaw, DecoratedCommand, DesignPayoffLine, DesignStaminaHypothesis, DesignedInheritance, Detestable, DiffDebugging, DirectingAttitude, DuckInterface, DynamicTypeCheck, DynamicTyping, EagerReadDerivation, EnablingAttitude, EncapsulatedCollection, EnterpriseArchitecture, ErraticTestFailure, EvansClassification, EventInterception, EventPoster, FirstLaw, FixedLengthString, FoundationFramework, GangOfFour, GetterEradicator, HarvestedFramework, HeaderInterface, HierarchicDataModel, HistoryIsNotBunk, HollywoodPrinciple, HumaneInterface, HumaneRegistry, IllustrativeProgramming, ImplicitInterfaceImplementation, InMemoryTestDatabase, IntegrationDatabase, InterfaceImplementationPair, InversionOfControl, JAOO2005, JunitNewInstance, LanguageForLearningObjects, LayProgrammer, LayeringPrinciples, LazyInitialization, LocalDTO, MakingStubs, MinimalInterface, ModelDrivenSoftwareDevelopment, MultipleCanonicalModels, NashvilleProject, NetworkDataModel, OOPSLA2004, OOPSLA2005, ObjectMother, ObservableState, OneLanguage, OpenInheritance, OutputBuildTarget, POJO, PatternShare, PatternsAreNothingNew, PostModernProgramming, PresentationDomainSeparation, ProjectionalEditing, ProtectedData, ProvideServiceStub, PublicCsharpFields, PublishedInterface, RelationalDataModel, ReportingDatabase, RepositoryBasedCode, RequestStreamMap, RoleInterface, RulesEngine, Seal, SecurityAndDesign, Seedwork, SegmentationByFreshness, SelfEncapsulation, SelfTestingCode, SemanticDiff, ServiceCustodian, ServiceOrientedAmbiguity, SetterInitialization, SmalltalkBooks, SoftwareDevelopmentAttitude, SourceBasedCode, SourceEditing, StaticSubstitution, StranglerApplication, SunkCostDrivenArchitecture, TechnicalDebt, TestCancer, TestDouble, TestDrivenDevelopment, TestInvariant, TestingResourcePools, TimeZoneUncertainty, TouchFile, Transactionless, TypeInstanceHomonym, TypedCollection, UbiquitousLanguage, UiPatternsReadings, UseOfXml, ValueObject, VotingMachines, Wardish, Web2.0, Xunit
| RequestStreamMap |
design |
1 July 2009 |
Reactions |
|
Hang around my colleagues at ThoughtWorks and you soon get the
impression that the only good Enterprise Service Bus (ESB) is a dead
ESB. Jim Webber refers to them as Egregious Spaghetti Boxes. So it's
not uncommon to hear tales of attempts to get them out of systems
that don't need them. Battle was joined at one client and it brought to mind my younger
days playing D&D. Webber swings but misses as the ESB is AC 2,
Evan gets a hit and rolls 2d8 for 6 damage. Erik finally kills it
by casting "Summon Request Stream Map". So what was Erik's
decisive spell? Essentially the idea was to take a simple request
and show how the data for the request and response made their way
through the layers of the application. Erik printed out all the code
that you needed to read to understand how this would work - which
ran to several pages. He also produced this diagram.  It's currently fashionable in agile circles to do Value Stream
Mapping as a way to uncover waste in a software development
process. I think of this as a request stream map because it similarly
takes a request and shows how it moves through the layers allowing
us to visualize what's going on and think about the cost and value of
the layers. Layering is an essential tool for building software
applications. But like most essential things in life, excess can
be almost as much of a problem as too little. A visualization like
this (or the multiple pages of code) can help you find where "just
enough" is. One hazard, however. If you do need to transform data from one
form to another, it's usually better to a few little
transformations than one big transformation. You want to avoid
unnecessary transformations not compress the ones you need.
|
| IllustrativeProgramming |
design |
30 June 2009 |
Reactions |
|
What's the most common programming language in the world? I'm not sure how you could go about measuring this, but one thing
you'd need to do is consider what we mean by programming. My
candidate answer considers that the most popular programming
language is one used widely by people who do not consider themselves
as programmers. This language is Excel, or more generally spreadsheets. Spreadsheets are easily used for small tasks, but are also used
for surprisingly complex and important things. Often I've seen
professional programmers gulp when they realize that some vital
business function is being run off some spreadsheet that they'd find
too complicated to muck with. In general, we've not had much success with programming languages
for these kind of LayProgrammers. Whenever someone talks about some
new environment that's going to allow people to specify complex
behavior "without programming" I mention COBOL, which was originally
designed to get rid of programmers. So it's important to consider
what Excel can teach us about programming environments. One property of spreadsheets, that I think is important, is its
ability to fuse the execution of the program together with its
definition. When you look at a spreadsheet, the formulae of the
spreadsheet are not immediately apparent, instead what you see is
the calculated numbers - an illustration of what the program
does.  Using examples as a first class element of a programming
environment crops up in other places - UI designers also have
this. Providing a concrete illustration of the program output helps
people understand what the program definition does, so they can more
easily reason about behavior. So why do I feel we need this particular Neologism?
Essentially because I think it deserves more thought. We pass by
illustrative programming examples without really thinking about them
or what makes them special - or even that they are special in some
way. We've used illustrative programming for years, but we've not
paid enough attention to it. We've not thought enough about what are
its essential qualities and what its strengths and weaknesses
are. I've chosen the term "Illustrative Programming" to describe this,
partly because "example" is so heavily used (and illustration isn't)
but also because the term "illustration" reinforces the explanatory
nature of the example execution. Illustrations are meant to help
explain a concept by giving you a different way of looking at it -
similarly an illustrative execution is there to help you see what
your program does as you change it. When trying to make a concept explicit like this, it's useful to
think about the boundary cases. One boundary is the notion of using
projections of program information during editing, such as an IDE that
shows you the class hierarchy while you are working on the code. In
some ways this is similar, as the hierarchy display is continuously
updated as you modify the program, but the crucial difference is
that the hierarchy can be derived from static information about the
program. Illustrative programming requires information from the
actual running of the program. I also see illustrative programming as a concept beyond the
classic REPL loop of dynamic languages. REPL loops allow you to
explore execution, but they don't make the examples front and center
in the way that a spreadsheet does its values. Illustrative
programming techniques put the illustration in the foreground of
your editing experience. The program retreats to the background,
peeping out only when we want to explore a part of the illustration. I don't think that illustrative programming is all
goodness. One problem I've seen with spreadsheets and with GUI
designers is that they do a good job of revealing what a program
does, but de-emphasizes program structure. As a result complicated
spreadsheets and UI panels are often difficult to understand and
modify. They are often riven with uncontrolled copy-and-paste
programming. This strikes me as a consequence of the fact that the program is
de-emphasized in favor of the illustrations. As a result the
programmers don't think to take care of it. We suffer enough from a
lack of care of programs even in regular programming, so it's hardly
shocking that this occurs with illustrative programs written by lay
programmers. But this problem leads us to create programs that
quickly become unmaintainable as they grow. The challenge for future
illustrative programming environments is to help develop a well
structured program behind the illustrations - although the
illustrations may also make us rethink what a well structured
program is. The hard part of this may well be the ability to easily create
new abstractions. One of my observations of rich client UI software
is that they get tangled because the UI builders think only in terms
of screens and controls. My experiments here suggest to me that you
need to find the right abstractions for you program, which will take
a different form. But these abstractions won't be supported by the
screen builder as it can only illustrate the abstractions it knows
about. My colleagues Rebecca Parsons and Neal Ford have been spending a
lot of time involved in thinking along these lines too. So here's
some thoughts that Neal had in an email exchange
- I think these tools work best for lay people (thus, your link
to LayProgrammers). However, in general, tools like
this slow down experienced/power users. When you mention UI
panels, the Mac is rife with these types of controls. I spend a
great deal of time in Keynote, fiddling with the inspector. At
least all those controls are in one place (not like the new ribbon
stuff). I would much prefer a markup language I could use to
directly define stuff, with macros, snippets, and all the other
things I'm accustomed to as a developer.
- as these tools grow, they get unwieldy (perhaps because they
are ceasing to be domain specific enough?) Look at Word, Excel,
and PowerPoint. They had to invent new UI metaphors to expose all
the functionality of those tools. APIs in programming languages
scale much better, with several orders of magnitude more density
before they become hard to navigate.
- All the best-practices and tools don't exist there:
refactoring, levels of testing, etc. Also, you loose the
connection to text, meaning that macro facilities either don't
exist or complex one-offs. I think a good comparison that
highlights the limitations of Illustrative Programming is the
comparison between bash (large, arcane, powerful, quirky) to
Automator. I almost never use Automator because it suffers from Dietzler's
Law: it's always lacking 10% of what I need. I gladly deal
with the crufty surface area of bash because of the more power
afforded.
- I share your bullishness around these types of tools, but they
are a long time from being useful for full-bore Agile
development. I hope they mature fast.
--Neal Ford
One of the few people to take illustrative programming seriously
is Jonathan Edwards. He's come up with many very imaginative ideas
as to what such an environment should look like. His vision of
illustrative programming is also closely bound to the notions of
projectional editing and controlled copy-and-paste. The trigger for me in wanting to coin a term here, is the use of
illustrative programming by Language Workbenches by people like
IntentionalSoftware. These Language Workbenches encourage
you to build illustrative DSLs. Using illustration is important in
this case since this should help engage lay-programmers, which is
one of the aims of using DSLs. The challenge is to do this without
falling into the trap of poor program structure.
|
| DynamicTypeCheck |
design |
2 June 2009 |
Reactions |
|
Recently some of our developers ran into the accusation that with a
dynamic language like ruby you use so many dynamic type checks that
you end up effectively writing your own type system. So they
thought, since we've written a lot of real ruby code - how often do
we make dynamic type checks? Michael Schubert gathered up the data. The table below contains the data. We define a dynamic type check
as the use of the methods is_a?, kind_of?,
and instance_of?. The lines of code come from the
standard rake stats command in rails.
| Project ID | Code | Test | LOC / type check | test LOC / code LOC |
|---|
type checks | Lines of Code | type checks | Lines of Code |
|---|
| A | 16 | 13318 | 0 | 9856 | 1448 | 0.7 | | B | 14 | 19138 | 0 | 17123 | 2590 | 0.9 | | C | 0 | 2607 | 0 | 2981 | ∞ | 1.1 | | D | 7 | 4265 | 3 | 4069 | 833 | 1.0 | | E | 32 | 29619 | 60 | 97688 | 1384 | 3.3 | | F | 18 | ~9500 | N/A | N/A | 528 | N/A | | G | 0 | 2455 | 0 | 3290 | ∞ | 1.3 | | H | 9 | 2220 | 6 | 6404 | 575 | 2.9 | | I | 23 | 10633 | 2 | 12331 | 919 | 1.2 | | J | 196 | 40461 | 24 | 88511 | 586 | 2.2 | | K | 17 | 5769 | 6 | 9848 | 679 | 1.7 |
The moral of this data is that you shouldn't expect to see a lot of
type check calls in your ruby code base. This, of course, is true of
any dynamic language. It was generally considered bad form in
Smalltalk circles I inhabited too. The methods that were checked for in this data aren't the only ones
that can be considered a dynamic type check. Other cases are
respond_to? and aClass === anInstance. Our
folks felt that these cases were no more common than the ones they
checked for. Most uses are those of dealing with liberal input - eg where a method
parameter can be a string, symbol, or array. These crop up in DSLish
situations where you want liberal input for the high readability.
|
| ContradictoryObservations |
design |
3 March 2009 |
Reactions |
|
Many computer systems are built to house data and turn it into
useful information for humans. When we do this there is a natural
desire to make that information consistent. After all what use is
there of a computer system that's in two minds about things? But sometimes computer systems should record contradictory
data and help humans deal with that. This issue came foremost to my
mind many years ago when working in health care for the UK National
Health Service. We were building a conceptual model for health care
delivery - essentially a conceptual schema for an electronic health
care record. Looking back on it, there were certainly plenty of things I'd
do differently now. But one thing in particular was something very
precious and important - the model was very much a collaborative effort
between myself, another software developer, two doctors and a
nurse. The clinicians understood the model and played a full part in
developing it - they were not merely passive reviewers. As a result
I think the ideas we developed were particularly valuable in
thinking about what a clinical practitioner wants to see in an
electronic health care record. One thing the clinicians were very strong about was this need to
capture contradictory information. I might have a note from the
Royal Hope Hospital saying my blood type is A and another note from
the Sisters of Plenitude saying my blood type is B. This would
clearly be nonsense, blood types don't change. But that doesn't mean
we cannot record these two bits of data. Without further
investigation we don't know which one is
correct. Even if we test again and confirm one of them, we
can't just throw away the bad one as it may have been the basis for
further clinical action. And of course there are lots of cases where the
contradiction isn't as clear cut. We may never be able to find out
which of two contradictory bits of data was wrong or may find a
change over time that is extremely unlikely but not impossible. The key to handling this issue is to represent my blood type
not as an attribute of a person class, but as a fully fledged class
in its own right - which we called observation. Each observation
applies to a particular patient, but also records such information
as when it was made, who made it, and how it was made.  We also saw that observations can be about the absence of things
as much as about their presence. So in some circumstances it may not
be possible to figure out my blood group, but it is possible to say
that it isn't blood group O. This we could represent as an
observation of an absence of blood group O. (I have no idea if this example
is possible or reasonable, but it can get tricky to think up
realistic examples quickly.) Often observing the absence of things
is crucial in a diagnostic process. Using observations changes the way we determine information about
a patient. Rather than simply asking for a patient's blood group, we
look at all the patient's blood group observations. If they are all
the same, then we just use that value. If they differ, we need to
delve deeper. In many cases observations do sensibly change over
time, so we might look at all the observations of my weight over
time to plot how my weight changes. Although we need to keep contradictory observations, we also
need to capture if we think one of them was wrong. Some observations, such
as a broken leg, will become untrue over time, but the blood group
example above is more likely to be a error. In the erroneous case we
have the notion of rejecting one observation with another. So we
might have a further test in the Albion Hospital that finds I'm
Blood Group A, this observation would then reject the Sisters of
Plenitude's observation. Rejecting an observation says that we believe it was
never true. We never delete the old observation, instead we mark it
as rejected and link it to Albion Hospital's observation.  An important property of information is that it's used to guide
behavior. A rejected observation may have been used as evidence for
further observations or to justify interventions. Keeping these
links in the record is essential since once an observation is
rejected we can then follow those links to investigate the
consequences. If the observation we've just rejected is a crucial
part of evidence for another observation, that should be questioned
and maybe rejected as well. Observations thus form a web of evidence
that we can examine as we learn more about the patient.  Most of the time, of course, we don't use complicated schemes
like this. We mostly program in a world that we assume is
consistent. But there are times where we have to step away from that
comfortable assumption. When that happens then explicit observations
are a useful tool (If you are interested in more of this, see Chapter 3 of Analysis Patterns. I'm
sure I'd write it better if I were to do it again now, but the core
concepts still seem to hold up pretty well. I'd also like to call out
my colleagues on this work: Tom Cairns, Anne Casey, Mark Thursz, and
Hazim Timimi)
|
| TechnicalDebt |
design |
26 February 2009 |
Reactions |
|
Update: Added a link to Ward Cunningham's video opinion You have a piece of functionality that you need to add to your
system. You see two ways to do it, one is quick to do but is messy -
you are sure that it will make further changes harder in the future.
The other results in a cleaner design, but will take longer to put in
place. Technical Debt is a wonderful metaphor developed by Ward
Cunningham to help us think about this problem. In this metaphor,
doing things the quick and dirty way sets us up with a technical debt,
which is similar to a financial debt. Like a financial debt, the
technical debt incurs interest payments, which come in the form of the
extra effort that we have to do in future development because of the
quick and dirty design choice. We can choose to continue paying the
interest, or we can pay down the principal by refactoring the quick
and dirty design into the better design. Although it costs to pay down
the principal, we gain by reduced interest payments in the future. The metaphor also explains why it may be sensible to do the quick
and dirty approach. Just as a business incurs some debt to take
advantage of a market opportunity developers may incur technical debt
to hit an important deadline. The all too common problem is that
development organizations let their debt get out of control and spend
most of their future development effort paying crippling interest
payments. The tricky thing about technical debt, of course, is that unlike
money it's impossible to measure effectively. The interest payments
hurt a team's productivity, but since we
CannotMeasureProductivity, we can't really see the true
effect of our technical debt. One thing that is easily missed is that you only make money on
your loan by delivering. Following the
DesignStaminaHypothesis, you need to deliver before you
reach the design payoff line to give you any chance of making a gain
on your debt. Even below the line you have to trade-off the value you
get from early delivery against the interest payments and principal
pay-down that you'll incur. (As far as I can tell, Ward first introduced this concept in an
experience report for OOPSLA
1992. It has also been discussed on the wiki.)
Additional Comments
Ward Cunningham has a video talk where he discusses this
metaphor he created. A couple of readers sent in some similarly good names. David
Panariti refers to ugly programming as deficit programming.
Apparantly he originally started using a few years ago when it fitted
in with government policy; I suppose it's natural again now. Scott Wood suggested "Technical Inflation could be
viewed as the ground lost when the current level of technology
surpasses that of the foundation of your product to the extent that it
begins losing compatibility with the industry. Examples of this
would be falling behind in versions of a language to the point where
your code is no longer compatible with main stream compilers." Steve McConnell brings out several good points in the metaphor,
particularly how keeping your unintended debt down gives you more
room to intentionally take on debt when it's useful to do so. I
also like his notion of minimum payments (which are very high to
fix issues with embedded systems as opposed to web sites). (Original posting 3 Aug 2004.)
|
| NashvilleProject |
design |
25 February 2009 |
Reactions |
|
I spent some time recently with one of my favorite ever
ThoughtWorks projects. It's a project that started in 1998, using
then new J2EE technology. Over the years it's had a fascinating
history: starting with EJBs, ripping them out, going offshore to
Bangalore, coming back to Chicago. Many people have moved in and out
of the project and the project has varied in head-count between 6 and
60. Overall the project has had over 300 staff-years of effort on
it and weighs in at around 100 KLOC. It's a favorite of mine because it exhibits an important property
of my preferred view of software development: a long term support of
a business function enabled by a well-designed code-base. The fact
that they are still adding useful business value after ten years is
an big dollop of kudos. They are able to rapidly add new features
when needed so haven't fallen into the typical morass of a legacy app. On this visit a couple of thoughts grabbed me. Firstly they've had an interesting evolution in their approach to
acceptance tests and how they update them as they add new
features. In their original (and common) world view, each time you
implement a new story you add one or more tests. This leads you to a
simple tracing structure where each story is verified by one or more
acceptance tests. But the problem with this approach is that over
time the tests grow in complexity with much duplication. In their new world view there is a suite of acceptance tests that
describe the application behavior in
SpecificationByExample style. Each time they play a new
story, they decide how to update this suite to reflect the new
behavior. This breaks the simple story-to-test relationship, but
results in a much simpler and coherent suite of tests. The second interesting aspect of the project is how it continues
to work at improving the code base. They came up with a good, if
informal, metric for describing this. A few years
ago, if they wanted to take on someone new they wanted that person
committed for at least a year, so they could get contributions that
would be worthwhile after coming up to speed on the code base. Now
that time is down to three months. For a ten year old app with that
many hands on it, that's quite an achievement. For me the key purpose of good design is that it allows you to
continue working rapidly with the code (the
DesignStaminaHypothesis). Assessing how long it takes a
developer to be productive with a code base is a good way to sense
this design quality. The minimum-commitment length metric is another
spin on this same idea. It's not something we can measure
objectively, but it is something that a team can consider looking
at. I'm hoping we'll get more people from the project talking about
their experiences. They did do a podcast last year (go to
thoughtworks podcasts and look for "Keeping Grey Code Fit").
|
|
|