Mavnn's blog

Stuff from my brain

API Design Workshop

Designing an API is hard.

You want to actually apply the principle of least astonishment - but you're the person who wrote the code. You're unlikely to be astonished. So you're trying to think how someone who didn't know what you know would think - which is never an easy starting point!

Similarly, you're trying to create the pit of success for users. Which means trying to make it very hard to do the wrong thing with your API. Preferably, in strongly typed languages, this should include using the type system to make illegal states unrepresentable so that code that compiles is very likely to work.

In general, the core libraries for .net are not bad at API design, but there are a few places where this isn't true. As an exercise, we at 15below are going to take one of them, split into teams and spend an hour or so seeing what alternatives we can come up with. Feel free to follow along at home, and if you do give it a try ping me a code snippet and I'll post it up with our internal attempts in a week or so.

Ecumenical APIs

One of the big sells of shared runtime functional languages such as F#, Scala and Clojure is that you can carrying on using the surrounding library ecosystem and your existing code. The different paradigm occasionally causes a little pain, but there are plenty of blog posts about how to wrap OO interfaces in a functionally friendly way.

This is not one of those blog posts. This is about making sure that your colleagues who are consuming your shiny new code in an imperative language (generally C# in my case) don't threaten to defenestrate you.

At 15below we've recently had need in some of our services of taking a distributed lock between servers. There are many services available designed for doing this, but after some deliberation we decided that we didn't want to add a new piece of infrastructure purely for this one purpose. So Sproc.Lock was born: SQL Server based distributed locking.

In this post, I'm not going to talk about the design of the service. What I'm going to write about is how I engineered the API to be pleasent to use from both C# and F#, giving a idiomatic interface from both languages.

LambdaCon 2015

This weekend I had the honour of speaking at LambdaCon. My own talk I'll be writing some separate posts on in the future, but I also wanted to jot down some notes on the conference before the memory faded.

Testing ProvidedType.fs by Example

The Type Provider Starter Pack was designed with two purposes initially. Firstly, to be a canonical repository for the ProvidedTypes files which provide a source file based API for creating type providers. And secondly, to be a set of tutorials and examples for people wanting to dip their toes into building type providers for the first time.

To be honest, it's not been doing a complete job of either:

  • I think most people are using it as the source of ProvidedTypes.fs and .fsi now days, but it didn't provide any infrastructure or testing for progressing the library.
  • The "examples" were limited to a link to my tutorial on building type providers

Today, that's changed. And I need your help!

Testing ProvidedTypes

Once I started thinking about it, it became clear that the code needed for basic type provider examples, and the code needed to test ProvidedTypes.fs were basically identical.

So I implemented a system for compiling and testing example .fsx scripts within the Starter Pack repository.

Want to help out? As long as you have some basic git and F# knowledge, it's easy!

Good Developer CVs (Résumés)

I've been reviewing a number of CVs from developers of varying experience recently, and wanted to get a few notes out there about what to do (and not do) on your CV if you want to get noticed.

Well, by me at least. Your mileage may vary with other reviewers! But - remember that you're interviewing the company as much as they're interviewing you. Hopefully this advice will get you more interest from the kind of companies you want to work for, even if it doesn't get you through the enterprise HR screen quite as often.

I'll start with the postives, and then go onto some things it's best to avoid.

Make yourself stand out!

Looking from the outside (at least, from the job descriptions!), 15below probably looks like a Microsoft shop. That's because it is - all of our new code is written in MS supported .net languages, we use SQL Server as our primary data store, WebApi, ASP.net MVC, etc.

But! One of the things that makes us who we are is that we've deliberately hired outside the box, especially for senior developer roles. I have professional Python experience, use F# by preference and run my personal computers on Linux. Three of us use Vim (or at least VsVim). One of us used to write C++ code for nuclear reactors (the testing is strong in that one). My colleagues tweet about Erlang, code for fun in Idris and steal ideas liberally from all these places to make 15below the kind of place it is. Not all of it makes it into production code, but even there you'll find technologies like RabbitMQ that don't show up that often in the Microsoft world.

If you tailor your CV to what it looks like we prefer, you immediately sink to almost invisibility. "Oh, an other developer with 5+ years C# experience who knows T-SQL and knows to buzzword Scrum onto the CV." To be clear: that's not a bad thing to have on your CV. It shouldn't be the only thing on your CV.

Show me the codez

This isn't essential, and you may not be able to depending on your previous employment. But if you can link to some previous code you've written (even a website where you can say "I wrote the JavaScript for this one") you'll immediately gain a boost towards an interview. We'll still give you the technical test, because unfortunately there are people who are stupid enough to try and pass off others work as their own (and it is stupid - you will get yourself burnt trying that). But you're much more likely to get to the interview stage. This is a particularly useful piece of advice for those of you just coming out of university. Can you link to your final year project? A hobby project? Your github account? If the code is good, you're instantly up there competing with candidates who on paper have years of experience over you.

Whether you show us code in advance or not, we will ask you to write something trivial during the interview. We'll make it as unscary as we can, but unfortunately enough people have come to us who cannot actually write code that we feel we have very little choice about this step. If you're nervous about this kind of thing, I would seriously suggest getting in a practice session or two with friends before getting to an interview for a development post. And be very, very wary of a company that doesn't ask you to demonstrate you can actually code.

Don't bother me with trivia, especially if it makes you look bad

Been out of school for more than 5 years? I couldn't give two figs about your grades. Unless you explicitly list the fact that you achieved a C and 3 E's at AS level. That gives me pause for thought. It's not a make or break thing, but no need to cut your own chances.

Don't fluff up your management skills (unless you want to be a manager)

We've been very borderline about interviewing several candidates because it was completely unclear from their CVs whether they actually write any code. Oh, it says "experienced C# software engineer" at the top of the CV, but if you look at the description of their last job it's all about "Team Leader", "Mentor", "SCRUM master"… Again, none of that's bad (well - maybe SCRUM master is a bit dubious) but we're not a Local Council where the only way of being "Senior" is to have management responsibility. We're hiring developers, and while we'll expect a senior developer to be a good communicator we're not expecting them to be graduates of Atlassian University. If you want to get into people management, it's a fine career path. If you want to be a developer, highlight your development skills.

Don't give in depth technical examples that give me the fear

We got a CV from someone who had recently written a desktop application that used every multithreading techniques. Really? All of them? In one app? An interesting design decision.

Fortunately for my curiosity, he proceeded to list every multithreading techniques:

  • ManualResetEvent
  • BackgroundWorker
  • Manual Dispatch

In 2015 this is not the way to highlight your expertise in writing asynchronous and concurrent reliable, maintainable code. If someone wrote code like this within the company, the code review would immediately result in some pair programming on "here's all the easier, more reliable ways you could have done this." From an applicant applying for a senior role it's an instant fail.

The problem is not here that someone didn't know how to write concurrent code: not every developer has reason to have learnt those skills. The problem is using a subject where you have very little expertise to try and boost your technical credentials.

Three things to take away here:

  1. Don't claim to be an expert on something unless you're certain that you actually are.
  2. Did something in a previous job in a way you wouldn't now? List the result, not the detail of the technique
  3. If you need ManualResetEvent you're probably doing something very wrong

Don't try and out Agile us

Yes, yes, we say we're an agile company. But frankly, a lot of stuff that people call agile is a complete waste of time, and other things we just can't do because we spend a lot of time working with customers who are not agile. At all. So, we do 15below agile. It's a nice shorthand to give you a good idea of how we generally work. And agile being as dispersed an idea as it is, that's going to be true of pretty much any company that says it's agile.

So be wary getting too carried away on your "agility". Again, don't claim to be expert if you're not: "we had a morning meeting everyday", as one of our candidates noted in his agile experience. And you're going to make me very wary if you try and come across as being an agile zealot - not because agile is bad, but because whatever it is you're a zealot about, it's not going to be "15below agile". And life's too short for getting into arguments about that kind of thing.


So, there you have. My own personal list of things that increase or decrease your chances that I'll recommend you're interviewed.

If you think that you'd be interested in working for the type of company that uses this type of criteria… well, oddly enough we're hiring at the moment. Ping me or drop a note through to [email protected]

Exploring Reactive Extensions

The Reactive Extensions project is "a library for composing asynchronous and event-based programs using observable sequences and LINQ-style query operators". That doesn't immediately give most people an intuitive grasp of exactly what it is - but it's a useful addition to the toolset so we put together a practical for people to experiment with.

At it's simplest, RX (as it's called… the Nuget package you're looking for is Rx-Main, obviously!) allows you to create an IObservable object which you can then… erm… observe.

Persistent Data Structures

In last week's Developer Education session at 15below we had a look at immutable and persistent data structures, and why you'd want to use them.

TL;DR version: are you writing performance critical, real time code? Do you have less memory available than a low end smart phone? No?

Use immutable data types everywhere you can.

The session was inspired by Scott Wlaschin's excellent is your programming language unreasonable? post. If you haven't read it yet, go and do so - it's much better than the rest of this post, and you can always come back here later if you remember.

One of the points that Scott raises is that code written with mutable data structures (ones that you can change after they've been created) is very hard to reason about. In the very literal sense of working out the reason why things happen.

Difficult vs Impossible

Although programming is young and we often don't know much about the "best" way to do things, we're not totally shooting in the dark.

Every so often, you come up against problems that people have investigated in detail, and given programming's mathematical roots this even leads on occasion to a proof about a certain type of system. It would be impossible to keep up with all of the research; but there are a few places where it's very helpful to know about general results.

I'm going to claim here that it makes a big difference to how you handle feature requests both as a developer, and as a business, when you're asked to produce systems which are actually impossible.

Let's take the one that comes up most often in my experience… Consistency in distributed systems.

So - this comes up the moment that somebody (customer, internal stakeholder, whatever) declares that having just a single service running on a single server is just not reliable enough. At some point, something will go wrong - and when it does, the service is a SPOF (single point of failure) and your processes which use it stop.

Unacceptable!

Every product owner, ever

"We must have a cluster!" the service developer is told. "Load balancing!"

"Hmmmm." says the developer to themselves. "Distributed computing. That can get a little tricky. Let's see if I can nail down the actual requirements a bit more."

The Q&A session goes something a bit like this:

  • Dev: So… Let's start easy and assume two nodes for now. How important is it that every write is replicated to both nodes before it's readable?
  • PO: Critical!
  • Dev: And… How important is it that the system stays available when one node is down?
  • PO: Critical!
  • Dev pausing: Ah… We won't be able to replicate writes at that point - the second node is down.
  • PO: Oh. Right, makes sense - read availability is critical though.
  • Dev: It would make life easier if writes can only be made to one of the two nodes - let's call it master. That OK?
  • PO thinks for while: OK. It's not ideal, but we've got a deadline. Go for it.
  • Dev: How about consistency - if Bob writes to the first node, and then immediately reads from the second, is it okay if he gets slightly out of date data?
  • PO: Absolutely not.
  • Dev: OK. Give me a moment.
  • PO: Just a moment - one last thing! This has to be super user friendly to use. So make sure it's completely transparent to the client consumer that they're talking to a cluster.
  • Dev: …right.

Little known to our PO, their requirements are at this point strictly impossible. The impossibility here is a particular edge case; what happens if the "master" node receives a write, sends it to the "slave" to replicate, but then never gets a response. What does it do? Return an error to the client? Well - no. If the slave comes back up, and the replication had been successful before the slave became unavailable, then we'd have an inconsistent history between slave and master.

Does it return a success? Well - no. In that case, we're violating our restriction that every write is replicated before it's considered available to read.

So it has to return something else - a "pending", "this write will probably be replicated some day" response. But that violates the restriction that it shouldn't add any complexity to the consumer. We now have a corner case that the server can't handle, so it has to be passed back to the client.

After this first write, we do have a little bit more flexibility - we can stop accepting new writes until we've heard from the slave that it's back up and available and just throw an error. But we're still left with that first, awkward write to deal with. (Perceptive readers will also realise that this set up actually leaves us less reliable for writes than a single node solution - proofs left as an exercise to the reader).

In reality, this impossibility is a subset of the more widely know CAP theorem: a distributed system cannot be always "Consistent" and always "Available" and still behave predictably under network "Partitions". The three terms in CAP have pretty specific meanings - check out a nice introduction at You Can't Sacrifice Partition Tolerance.

This is the point where reality diverges, Sliding Doors style, depending on what the developer does next. The branches are numerous, but let's have a look at some of the most common. As an aside, I've fallen into pretty much all of these categories at different points.

Option 1: The developer doesn't know this is impossible either

At this point, we end up with a response that goes something along the lines of: "Well - I can do you a temporary solution where we return a pending result in situation x. Bit of a pain; put it on the technical debt register, and we'll sort it out when we have a bit more time."

Or: "I can't think of a completely fool proof solution right now; how about in situation x we return a failure for now. It'll be a bit confusing when a user gets told the write failed, and then it shows up later - but we'll get it sorted before the final release."

Neither of these solutions are wrong, as such: but the building of impossible expectations will inevitably sour the relationship between product owner and developer, and can cause serious business issues if an external customer has been promised impossible results. There may even be direct financial penalty clauses involved.

Option 2: The developer knows it's impossible, and thinks the product owner does too

Here the developer says "Well, I can return a pending result…" and the PO adds mentally "…which is a OK stop gap measure, I'll schedule some time to clean it up later."

This leads to pretty much the same outcomes as "Option 1", except the developer gets an unhealthy injection of smug self-righteousness for knowing that he never promised the impossible. In general, this is not helpful.

Option 3: The developer knows it's impossible, tries to explain… And fails

This is very similar in outcome to Options 1 & 2. Just more frustrating to the developer, especially if the product owner then claims the developer is "negative" or "incompetent".

Option 4: The developer knows it's impossible and explains to the product owner how and why

This is hard on two levels. On the first: the proof of why something can't be done might be genuinely difficult to understand. On the second: it can be hard to work out if you've avoided Option 3, or if people are just nodding and smiling.

We nearly hit one of these scenarios this week; fortunately our QA department spotted the mismatch in expectations (yay QA!). Where things got a bit strange is that it was raised as Option 3: "hey! Can we put a bit more effort in, and make this nicer to use?" At the QAT phase this much easier to deal with though - you don't have angry customers, commercial agreements and these other bits hanging over your heads (well - not if you're writing an internal service anyway).

What can we take away from all of this?

A few things.

Developers

  1. As a developer, you must know the basics of the domain you're working in. Keep on learning, folks.
  2. You must be able to communicate as a developer. A lot of developers are introverts, myself included. This is not an excuse. Introvert means that you can't recharge around other people, not that you can't talk to them.
  3. You cannot remove your developers from your customer communications, or completely separate commercial proposals and technical evaluation. You must have technical input into your business process, because sometimes its isn't a question of how much time you spend, how well you design or how skilled a developer you assign to the problem: it might just be impossible.

"Product Owners" (whatever your actual job title is)

  1. Listen to your developers, and pay attention to the wording. If they say something is impossible (not hard, not delayed) check you understand why.
  2. Be careful how you define the business problem to your developers. You may end up specifying a problem that is unsolvable if you end up layering up too many technical restrictions - while your developer may be able to suggest something that meets the business criteria without falling foul of technical (or more importantly mathematical) limitations to what is possible.
  3. If you place a technical requirement ("it must be clustered - no single points of failure!") make sure you understand the technical trade offs that you are imposing. This may take a long time. Alternatively, and preferably, rephrase your requirement to be your actual business requirement ("We promised 98% uptime - what's your design to make sure it happens?").
  4. You must be able and willing to say "no" to a customer when they ask for something impossible. You can offer alternatives, work arounds - but don't promise the impossible. It will come back, and it will hurt you.

Keeping Up With the Latest Hammer

Making Sure Your Developers Keep Developing

Software development is a strange profession, mostly because it's so young and because the tools are changing so fast. I've not been an established enough craftsman in any other trade to know whether other professions are moving as quickly these days, but at least in my imagination once a carpenter learns to use a hammer, it doesn't get discontinued after 2 years and the hammer taken off the market. Or a new hammer released that hammers nails ten times as fast, but has a different shaped handle and you have to hammer sideways instead of down.

We don't even seem to be able to decide whether it's a craft or a science. You can earn Computer Science degrees - but then well known software professionals choose titles like Software Craftsman.

Despite all of our claims of best practice and shared knowledge, it largely boils down to: developers don't know what they're doing yet. We're a new profession, and we're still learning - not just as individuals, but as a profession.

This means that both as an individual developers, and as software houses - if we stop learning, we sink. The competitive advantage of keeping up with what's happening in the industry so outweighs the cost of doing the research that it would be foolish not to. Because while we might not yet know the right way to do things, we're still definitely finding better ways to do things.

So: how do we do keep up to date, as individuals and as companies?