The next amazing layer, now that we’ll have plugin architectures worked out, is to use plugins as stand-ins — very lightweight simulators — of the upstream behavior.
A "stand-in" is a plugin that pretends to be an upstream service expressly for the purpose of developing a particualr downstream or set of downstreams that operate against it. Now, the mind can’t help to go to the word "simulator", I even used it myself up above. But I’m going to use "stand-in" from now going forward.
This is for similar reasons to why I use and advocate "microtest" over "unit test". All microtests are unit tests, but all unit tests aren’t microtests. All stand-ins are simulators are not all simulators are stand-ins.
I really want to be very careful with this. "Simulator" evokes the idea of a kind of byte-for-byte replacement for a service. It carries notions of deep verisimilitude: of generality, breadth, and complexity. I want to step way way way back from all that.
Why not "simulator"? I’ll tell you something they don’t teach in school: People who’ve ever written or relied heavily on simulators avoid them like the plague. They have the thousand-yard stare. They start at loud noise, often fiddling with their hands in calming patterns. Simulators are — not always, but nearly so — awesome in theory and horrific in practice. And the key reason? The extraordinary weight of achieving verisimilitude.
So. Stand-ins. Think of a downstream and its upstreams for a minute. Notice a few aspects, not of either side, but of the conversations themselves.
Here’s a typical one. My app needs to know the previous ten blue dates before an arbitrary other date. (No, not blue, of course, but I am under NDA.) It gets that from a Blue Service. It says, "Hey, here’s a date, what are the last ten blue dates before it?"
Now, two things. 1) calculating blueness of dates is a nasty business. 2) the blue service does about 50 other cool things around blue dates than just tell you the last ten ones.
But now two things about my app: 1) It doesn’t care that the blue dates are really blue. It just needs ten dates it can pretend are blue. 2) It could give a shit about anything else the blue service does. A perfectly legitimate stand-in for BlueService for that downstream’s purpose would fit in five lines of java: take the input date, and return the prior ten dates, maybe with a hardwired gap or two in it.
Buh-dump-bump. And that’s the BlueService plugin.
Another case: "what are the available options on this widget?" The OptionsService looks at the widget’s id: mod it by 4. If it’s 0, say you don’t know the widget. if it’s 1, give an empty set. 2’s a singleton set, 3’s a multi-set.
So those two cases give us two examples of typical stand-in behavior. Those of you who are used to microtested TDD will be comfortable with this. (Those who aren’t, it’s time to get comfortable with it. Can’t review the arguments just now.)
Now, what about commands, like "finalize this order"? The simplest possible stand-in says "ok". But what about multiple commands. "finalize this order" followed by "is this order finalized?" or even "give me that order i just finalized and let me prove it’s finalized manually".
Part of the answer is "you might be doing this wrong", but I’m going to sidestep that part for the moment. Make the stand-in remember in memory what you told it. Oh, "in memory", did I forget to mention that? Oh, hell yeah. No multi-run persistence. You’re edging in to simulator-territory there, and we really don’t want that.
About the heaviest we’ll get: have two services in one stand-in, and let the stand-in endpoints use the same data. And we’d consider that heavy. Here’s the thing, generalized programming languages are good at connecting data in memory in easy ways. Way easier than SQL is, frankly.
So we’re throwing away lots of responsive logic, inter-run persistence, opaque data, validation, security, and so on. This thing is one shitty simulator!
Good.
The very low fidelity is what makes them so easy to knock out.
(Just noticed I forgot one of the key hacks for low-fi stand-ins: dramatically narrow the dataset. My bad.)
Okay, I have three more points to make about this thing. 1) there are reasons not to do this inside our downstream process, 2) we can add stand-in control endpoints, 3) to really take advantage of these ideas, we need to change our dev process.
Any good microtester will have already done this kind of thing a time or two, only hacking it in one layer further back, by providing a FakeBlueServiceClient object, say. That’s an in-process fake. So why don’t we do that?
Four reasons. 1) we’d have to write it instead of the upstream team. 2) getting the downstream’s transport layer right is often a big part of the job. 3) other downstreams of that upstream won’t be able to use it in a plug and play manner. 4) traffic won’t render in UDispatch.
Next, what’s this about stand-in control points? We can add endpoints to do things like: give a one-time hardwired response, reset/save/load the dataset, create transport layer problems like timeouts or refuses or 500’s.
All of those can come for either free or cheap. transport errors are super-generic. data resets/saves/loads can be part of the stand-in’s plugin interface, and so on. These give the downstream developer enormous ability to TDD their end, at speed.
Finally, changes to the dev process.
I totally fooled you: that’s for "coming soon to a muse near you".
This is going to mean changing our practice. Things that change our practice have to be incremental and iterative, and produce a steady flow of better. Yikes.
So, gorgeous weather for a 200-person outdoor party, and my aging hippie community is having its 45th birthday bash today.
I’m not a party person, will just make a cameo, but I’m still pleased by it all.
I hope you also have a gorgeous day that pleases you, even indirectly!