Sunday, June 30, 2013

Multiplayer Enterprise Architect

Hanging around in the pub after DDDX, I ended up talking to Alberto Brandolini. For those who have never met him; he's very much into visualization. You will always see him carrying a drawing pad, with a dash of permanent marker on his cheek, and a few lost sticky notes on his back. I don't know if it was the Italian accent and the strong gestures, or my mildly intoxicated condition, but the idea of visualization as an important tool grew on me even more that evening.

During the conversation Alberto shared a recent experience where he was brought in as a facilitator to assist in planning multiple projects spread over a bunch of teams spanning multiple months. The bottom line was that with a big wall of paper, some hand drawn boxes, and lots of sticky notes, they had enabled intense and efficient collaboration, achieving what upper management had expected to take weeks, in a single day.

In my current project, we also could have taken advantage of these tools. Early on in the project, a team member was charged with the task of documenting all existing payment file transfers in the company. This isn't as trivial as it sounds though; these files come in through a handful of specialized secure gateways, are then transferred internally by a few different propriety transfer platforms - of which some still stem from the mainframe era, to be processed, and returned to the customer through one of the gateways in one of the available output formats. To make matters worse, all of these systems are owned by different teams.
We probably tackled this in the worst way possible; with a spreadsheet and meetings. We could have tried occupying a meeting room, plastering the wall with paper; drawing all the systems on, and inviting all involved teams to come in and help us visualize all the transfers between systems. We could draw arrows between the systems, and stick post-it notes on top that represent a file with its constraints (source and destination folders, schedules etc...).
Too late for that now, but I'm curious to set out such an experiment in the future.

As you're reading this, I'm traveling for three weeks - which I'll surely write about, and in my backpack are two books that should help keep me occupied during the flight, and long overlays: Visual Meetings, and The Back of the Napkin. I'm confident there's a lot of value to be found in visualization, either to make sense of a complex problem, or as a tool to support collaboration. Going forward I plan to get a lot better at it.

Sunday, June 23, 2013

Not handling edge cases, making them explicit instead

When I wrote about accidental entities earlier, we followed a consultant building software for a car rental company. In the meanwhile, he has finished implementing the registration of new cars. Next on the list is allowing customers to make a booking.

We managed to get the CEO to set a whole hour apart to walk us over how the booking system should work.

CEO: "I'm not sure this meeting is going to take a whole hour though. Making a booking is rather trivial. Do you have any idea on how a booking would work?"
Us: "Well, as far as I understand - and don't be too hard on me - a customer makes a booking, we allocate the car for the requested period, send the customer a confirmation email and we're done."
CEO: "Hold your horses, it's not that trivial. When we receive a booking, we first need to verify the customer's credit card."

Us: "How do I verify a credit card?"
CEO: "You don't. We use a third party electronic verification system that does just that; they check if the credit card is valid and has sufficient credit."

Us: "What happens if the credit card is declined?"
CEO: "That's easy; we just cancel the booking, and inform the customer."

Us: "And what happens when the credit card gets verified?"
CEO: "Then we approve the booking."

Us: "Let me stop you right there. Should we allocate the car immediately as soon as the customer makes the booking?"
CEO: "Hell no! Verification of the credit card can take a while; we might lose business if the credit card turns out to get declined."

Us: "Hmmm, what happens if someone else has made a booking for the same car in the meanwhile?"
CEO: "That's a good question. In the past, we've hardly encountered this problem, but it does happen though; we should probably take care of this edge case."

Us: "How do you want to take care of these double bookings? I can imagine you don't just want to cancel the second booking, and lose business over this, right?"
CEO: "No, exactly; I like your thinking! There are a few options here; if they only overlap for 30 minutes or so, we don't do anything. When the customer comes to get his car, we apologize for the small delay and offer him a frappuccino to soothe him. If the overlap is bigger, and we have bigger or more expensive cars just sitting in the parking lot for that period, we often give the customer a free upgrade. When we don't have any expensive cars left, we let our best sales person call the customer, and try to work something out. Sometimes we give them a small discount, and they'll happily reschedule their plans a few hours instead of having to look for another rental car. We try to avoid canceling a booking as much as possible; we want to build a reputation where we always deliver."
Us: "Wow, interesting. It's going to cost me some time to get that right though. You said that this almost never happens. Since deciding on a good solution for the double booking seems to be non-trivial, how about we don't handle this edge case right now, but make it explicit instead? We can mark the booking as double, make the system raise its hand, and ask for human intervention. Backoffice users already have the tools, and have the experience to decide on the best solution for the double booking. Leaving this out for now would also make it possible to go to market a few weeks earlier." 
CEO: "I really like this idea; let's keep our first version as lean as possible. It really doesn't make much sense trying to automate this right off the bat. It's not like you're cheap, you know. It would be useful for the system to keep count of every double booking though. Can we do that?"
Us: "Yep, that shouldn't be too hard."

After this talk with the CEO, we set out to model the solution in code. 

In our first iteration, a booking has multiple representations, a representation for each state. One of the advantages of modeling it as such, is that it allows us to make all possible state changes per representation explicit; you can't accept a booking if the credit card hasn't been approved etc... Alternatives could be the classic state pattern, or something workflowish, but that isn't really the focus of this post.
var booking = new Booking(
    bookingId, carId,
    new Customer(new CreditCard("343705171682875"), new CustomerName("Jef", "Claes")), 
    new Period(DateTime.Now.AddDays(1), DateTime.Now.AddDays(4)));

var bookingWithVerifiedCreditCard = booking.CreditcardVerified();
var doubleBooking = bookingWithVerifiedCreditCard.Double();
Each state change triggers an event. If we output each event, and run the snippet above we end up with this.
(EVENT) BookingCreditCardVerificiationPending
(EVENT) BookingCreditCardVerified
(EVENT) DoubleBooked
The BookingCreditCardVerified event triggers us to detect doubles.
public class BookingCreditCardVerifiedHandler : IHandle<BookingCreditCardVerified> 
{    
    private readonly IBus _bus;

    public BookingCreditCardVerifiedHandler(IBus bus)
    {
        _bus = bus;
    }

    public void Handle(BookingCreditCardVerified @event)
    {
        _bus.Send(new DetectDoubleBooking(@event.BookingId));
    }
}
If a double booking is detected, we want to make the system raise its hand, and notify a human. This can be done through an email, a notification in the backoffice portal, or whatever really.
public class DoubleBookedHandler : IHandle<DoubleBooked>
{
    public void Handle(DoubleBooked @event)
    {
        NotifyHumans();
    }

    private void NotifyHumans() { }
}
Although, the technical implementation isn't very special, I think it does show how events can help support the language and distribute responsibility to where it belongs.

There is this misconception that because we now have computers, they should solve all our problems, even all the edge cases. Edge cases - by definition - only happen at extreme conditions, and are regularly hard to take care of in a satisfactory manner, without a considerable investment. By making edge cases explicit, we allow a human to intervene, and decide on the best solution for the problem. This way we can go to market more quickly, with less code, and we might ironically even end up with happier customers. By collecting statistics on how often these edge cases occur, we can make a better informed decision on whether it's worth the investment.

Sunday, June 16, 2013

Feeds worth reading - saying goodbye to Google Reader

New to programming in the wild, and starving for hints on how to do it better, I would often stumble upon programmers documenting their journey on a personal blog. A big orange icon would incite readers to subscribe to their content using RSS. Taking the bait, I started using Google Reader, and found myself frantically hoarding feeds a bit later.

After a while I got fed up with going through an abundance of subscriptions daily though; it was turning into a chore. Instead of forsaking RSS, I set myself to aggressively cut down the number of feeds I subscribe to - RSS is too powerful to just dismiss. A feed I subscribe to should be fairly consistent at making what I read worthwhile. I even adapted the practice of putting a feed in quarantine before it makes it into my permanent reading list.

Being a loyal Reader user for five years, I was startled when Google announced the end of Reader. Although I somewhat understand Google feels the need to focus on a limited set of products, condemning Reader to the graveyard must hurt the trust of an influential customer segment; tech-savvy people. I for one feel less and less confident hosting my writings on Blogger, and I will definitely think twice before I take a dependency on another Google service in the future. I take comfort in the thought that Google is less likely to kill a service where they have been trusted with content that lies very close to a customer's heart, and that when they do close the books, they have a reputation of making your way out as smooth as possible.

Now that July is creeping up on us, I have to get off the fence, and elect a Google Reader successor. When I asked the Twitter crowd where they migrated to, Feedly seemed to be the consensus. I had a look at its homepage, but not offering a browser based version is a showstopper for me. I then read this Lifehacker article, and decided on trying The Old Reader, which doesn't seem to move my Google Reader cheese too much.

Preparing the migration, I took five minutes to push my OPML file, and a more readable markdown representation to Github. Find my curated list of feeds I consider worth reading here.

I'm still looking for resources on life in general and functional programming that can positively influence me. If you have any recommendations please let me know!

Sunday, June 9, 2013

Angular.js and IE8 caching

Older Internet Explorer versions are notorious for agressively caching AJAX requests. In this post, you'll find two techniques that combat this behaviour.

The first option is to have your server explicitly set the caching headers.
Response.Cache.SetExpires(DateTime.UtcNow.AddDays(-1));
Response.Cache.SetValidUntilExpires(false);
Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetNoStore();
Since you don't necessarily own the server, or clients might already have cached some requests, you can trick the browser into thinking each request is a fresh one by making each url unique. Our old pal jQuery already learned this trick years ago. Angular.js on the other hand seems to have forgotten. We can get around though.

If you merge this pull request (or wait for angular.js version 1.2), you will find angular's HTTP provider augmented with request interceptors, enabling you to mold the request before it goes out.

The interceptor we're adding to kill the cache only touches GET requests, appending a 'cacheSlayer' querystring parameter with a timestamp to each url, making it unique and thus bypassing the cache. A factory is responsible for creating it, while a config block pushes it into a collection of interceptors.
var AppInfrastructure = angular.module('App.Infrastructure', []);

AppInfrastructure
    .config(function ($httpProvider) {
        $httpProvider.requestInterceptors.push('httpRequestInterceptorCacheBuster');
    })    
    .factory('httpRequestInterceptorCacheBuster', function () {
        return function (promise) {
            return promise.then(function (request) {
                if (request.method === 'GET') {
                    var sep = request.url.indexOf('?') === -1 ? '?' : '&';
                    request.url = request.url + sep + 'cacheSlayer=' + new Date().getTime();
                }

                return request;
            });
        };
    });    
I hope this helps someone spending time on more important matters. 

Sunday, June 2, 2013

Accidental entities - what about the UI?

This post is a follow-up to my previous blog post "Accidental entities - you don't need that identity".

In that post, we followed a consultant building an application for a car rental. One of the requirements was that the CEO could manage a collection of available colors. Although the tools at our disposal - a relational database and NHibernate - wanted to trick us into making a car reference one of these available colors by its identifier, we found out that the CEO really thinks of a car's color as a value, and does not care about a color's identity. This means that we didn't make a car reference an available color, but we copied its value instead. This allows the CEO to remove available colors, without it having an impact on cars that already came in that color.

The solution we're building contains a public facing web application that allows customers to make reservations online, and a backoffice web application - hosted on the intranet, that employees will use to manage the cars.

When a new car arrives at the car rental, a backoffice user will register it. Once the car is registered - and in use, backoffice users should still be able to change some of its characteristics; brand, model, color, engine size, etc...

Instinct tells us to add a page that enables editing all the car's properties. Some of these properties are free text, some radio buttons, but for the car color, it's a dropdownlist.
Halfway through implementing this new functionality, we notice that changing the color gets us into trouble. We populate the dropdownlist with all available colors, but when we want to bind the car's current color as the selected value, it's not in the list of available colors. The CEO has removed the car's current color out of the list of available colors.
After a bit of tinkering, we come up with a workaround that adds the car's current color to the available colors with a default value of -1. This allows us to determine if the color needs to be changed.


Relational databases, RAD tools, scaffolding and anemic models have poisoned our minds, making us throw up the database schema all over the UI. We can do a lot more though.
If we take a step back, and make an effort to discover what changing these properties really means to our business users, we might come up with a totally different user experience.

We leave the UI as is, and ping the CEO on Lync, inviting her for a coffee break.

After a bit of obligatory small talk, we start asking questions about what we're really after.

Us: "Can all of the car's characteristics change after the initial registration?"
CEO: "No, have you ever seen a car change brand, or model through its lifecycle?"

Us: "I can imagine the engine size also belongs to the list of characteristics that can't be changed after registration?"
CEO: "Oh, it can! We sometimes have the engine of our sports models tuned to have an edge over the competition. Guys are crazy for horsepower."

Us: "What about changing the color?"
CEO: "If a car has some nasty scratches on it, we sometimes get it repainted. If we do get it repainted, it's always in one of the colors available at that moment."

We return to our desk with a far better understanding of what it really means to change each of the car's characteristics. We discovered a whole new language, with some important constraints. Neither brands nor models change after registration. Engines get tuned, increasing their engine size. A car's color doesn't change; a car gets repainted - always in one of the available colors.

After iterating on this feedback, we're far more satisfied with the model; it captures the language far better. Our backend implementation is not the most important part of the solution though; it's useful to invest in improving the UI.

After some experimentation we come up with a more task-based UI, something that looks like this.


The UI now does a far better job supporting the language and communicating its constraints. And with that, we also solved the original problem that motivated us to ask these extra questions. There is no value in having the current color in the dropdownlist; we are better off making the process of having a car repainted explicit.

When we make an effort to really capture the language, this will be reflected in our model, but also in the UI. This can add tremendous value. It's not only about making the user experience more intuitive, but about making a language consistent for thousands of users, supporting processes and communication throughout the company.

As a disclaimer; sometimes it's fine to just throw up your database. There's a place and time for anything. But don't let CRUD be the only tool in your toolbox; you can do much more. Make sure to invest this extra effort where it matters, where it makes a difference.