REST lesson learned: Consider a self link on all resources

Friday, 03 May 2013 10:32:00 GMT

Add a self-link on RESTful resource.

This suggestion is part of my REST lessons learned series of blog posts. Contrary to previous posts, this advice doesn't originate from a lesson learned the hard way, but is more of a gentle suggestion.

One of the many advantages of a well-designed REST API is that it's 'web-scale'. The reason it's 'web-scale' is because it can leverage HTTP caching.

HTTP caching is based on URLs. If two URLs are different, they represent different resources, and can't be cached as one. However, conceivably, a client could arrive at the same resource via a number of different URLs:

  • If a client follows redirect, it may not arrive at the URL it originally requested.
  • If a client builds URLs from templates, it may order query string parameters in various ways.
If your API serves representations of the same resource for too many URLs, it could hurt cacheability. It may also confuse clients.

Consider, as courtesy, adding a self-link to all resources. Adding a self link is as easy as this:

<product xmlns="http://fnaah.ploeh.dk/productcatalog/2013/05"
         xmlns:atom="http://www.w3.org/2005/Atom">
  <atom:link href="http://catalog.api.ploeh.dk/products/1337"
             rel="self" />
  <atom:link href="http://catalog.api.ploeh.dk"
             rel="http://catalog.api.ploeh.dk/docs/rels/home" />
  <atom:link href="http://catalog.api.ploeh.dk/categories/chocolate"
             rel="http://catalog.api.ploeh.dk/docs/rels/category" />
  <atom:link href="http://catalog.api.ploeh.dk/categories/gourmet"
             rel="http://catalog.api.ploeh.dk/docs/rels/category" />
  <name>Fine Criollo dark chocolate</name>
  <price>50 DKK</price>
  <weight>100 g</weight>
</product>

Notice the link with the rel value of "self"; from its href value you now know that the canonical URL of that product resource is http://catalog.api.ploeh.dk/products/1337.

Particularly when there are (technically) more than one URL that will serve the same content, do inform the client about the canonical URL. There's no way a client can tell which URL variation is the canonical URL. Only the API knows that.

Redirected clients

A level 3 RESTful API can evolve over time. As I previously described, you may start with URLs like http://foo.ploeh.dk/orders/1234 only to realize that in a later version of the API, you'd rather want it to be http://foo.ploeh.dk/customers/1234/orders.

As the RESTful Web Services Cookbook explains, you should keep URLs cool. In practice, that means that if you change the URLs of your API, you should at least leave a 301 (Moved Permanently) at the old URL.

If you imagine a mature API, this may have happened more than once, which means that a client arriving at one of the original URLs may be redirected several times.

In your browser, you know that if redirects happen, the address bar will (normally) display the final URL at which you arrived. However, consider that REST clients are applications. It will be implementation-specific whether or not the client realizes that the value of the final address isn't the URL originally requested.

As a courtesy to clients, do consider informing them of the final address at which they arrived.

URL variations

While the following scenario isn't applicable for level 3 RESTful APIs, level 1 and 2 services require clients to construct URLs from templates. As an example, such services may define a product search resource as /products/search?q={query}&p={page}. To search for chocolate (and see the third (zero-indexed) page) you could construct the URL as /products/search?q=chocolate&p=2. However, most platforms (e.g. the ASP.NET Web API) would handle /products/search?p=2&q=chocolate in exactly the same way. The more query parameters you allow, the more permutations are possible.

This is a well-known problem in search (and computer science); it's called Canonicalization. Only the API knows the canonical value of a given URL: do consider informing the client about this value.

Summary

Adding a self-link to each resource isn't much of a burden for the service, but could provide advantages for both the service and its clients. In most cases I'd expect the ROI to be high, simply because the investment is low.


REST lesson learned: Consider a home link on all resources

Thursday, 02 May 2013 10:51:00 GMT

Add home links on RESTful resources.

This suggestion is part of my REST lessons learned series of blog posts. Contrary to to the previous posts, this advice doesn't originate from a lesson learned the hard way, but is more of a gentle suggestion.

When designing a level 3 RESTful API, I've found that it's often helpful to think about design issues in terms of: how would you design it if it was a web site? On web sites, we don't ask users to construct URLs and enter them into the browser's address bar. On web sites, users follow links (and fill out forms). Many well-designed web sites are actually HATEOAS services, so it makes sense to learn from them when designing RESTful APIs.

One almost universal principle for well-designed web sites is that they always have a 'home' link (usually in the top left corner). It makes sense to transfer this principle to RESTful APIs.

All the RESTful APIs that I've helped design so far have had a single 'home' resource, which acts as a starting point for all clients. This is the only published URL for the entire API. From that starting page, clients must follow (semantic) links in order to accomplish their goals.

As an example, here's a 'home' resource on a hypothetical product catalog API:

<home xmlns="http://fnaah.ploeh.dk/productcatalog/2013/05"
      xmlns:atom="http://www.w3.org/2005/Atom">
  <atom:link href="http://catalog.api.ploeh.dk/products/1234"
             rel="http://catalog.api.ploeh.dk/docs/rels/products/discounted" />
  <atom:link href="http://catalog.api.ploeh.dk/products/5678"
             rel="http://catalog.api.ploeh.dk/docs/rels/products/discounted" />
  <atom:link href="http://catalog.api.ploeh.dk/products/1337"
             rel="http://catalog.api.ploeh.dk/docs/rels/products/discounted" />
  <atom:link href="http://catalog.api.ploeh.dk/categories"
             rel="http://catalog.api.ploeh.dk/docs/rels/product/categories" />
</home>

From this (rather sparse) 'home' resource, you can view each discounted product by following the discounted link, or you can browse the catalog by following the categories link.

If you follow one of the discounted links, you get a new resource:

<product xmlns="http://fnaah.ploeh.dk/productcatalog/2013/05"
         xmlns:atom="http://www.w3.org/2005/Atom">
  <atom:link href="http://catalog.api.ploeh.dk"
             rel="http://catalog.api.ploeh.dk/docs/rels/home" />
  <atom:link href="http://catalog.api.ploeh.dk/categories/chocolate"
             rel="http://catalog.api.ploeh.dk/docs/rels/category" />
  <atom:link href="http://catalog.api.ploeh.dk/categories/gourmet"
             rel="http://catalog.api.ploeh.dk/docs/rels/category" />
  <name>Fine Criollo dark chocolate</name>
  <price>50 DKK</price>
  <weight>100 g</weight>
</product>

Notice that in addition to the the two category links, the resource representation also contains a home link, enabling the client to go back to the 'home' resource.

Having a home link on all resources is another courtesy to the client, just like avoiding 204 responses - in fact, if you can't think of anything else to return instead of 204 (No Content), at the very least, you could return a home link.

In the above examples, I didn't obfuscate the URLs, but I would do that for a real REST API. The reason I didn't obfuscate the URLs here is to make the example easier to understand.


REST lesson learned: Avoid hackable URLs

Wednesday, 01 May 2013 10:29:00 GMT

Avoid hackable URLs if you are building a HATEOAS API.

This is a lesson about REST API design that I learned while building non-trivial REST APIs. If you provide a full-on level 3 REST API, consider avoiding hackable URLs.

Hackable URLs

A hackable URL is a URL where there's a clear pattern or template for constructing the URL. As an example, if I present to you the URL http://foo.ploeh.dk/products/1234, it's easy to guess that this is a resource representing a product with the SKU of 1234. If you know the SKU of another product, it's easy to 'hack' the URL to produce e.g. http://foo.ploeh.dk/products/5678.

That's a really nice feature of your API if you are doing a level 1 or 2 API, but for a HATEOAS API, this defies the purpose.

The great divide

Please notice that the shift from level 2 to level 3 RESTful APIs mark a fundamental shift in the way you should approach URL design.

Hackable URLs are great for level 1 and 2 APIs because the way you (as a client) are told to construct URLs is by assembling them from templates. As an example, the Windows Azure REST APIs explicitly instruct you to construct the URL in a particular way: the URL to get BLOB container properties is https://myaccount.blob.core.windows.net/mycontainer?restype=container, where you should replace myaccount with your account name, and mycontainer with your container name. While code aesthetics are subjective, it's not even a particularly clean URL, but it's easy enough to produce. The URL template is part of the contract, which puts the Windows Azure API solidly at level 2 of the Richardson Maturity Model. If I were designing a level 1 or 2 API, I'd make sure to make URLs hackable, too.

However, if you're building a level 3 API, hypermedia is king. Clients are expected to follow links. The addresses of resources are not published as having a particular template; instead, clients must follow semantic links in order to arrive at the desired resource(s). When hypermedia is the engine of application state, it's no good if the client can short-circuit the application flow by 'hacking' URLs. It may leave the application in an inconsistent state if it tries to do that.

Hackable URLs are great for level 1 and 2 APIs, but counter-productive for level 3 APIs.

Evolving URLs

One of the main attractions of building a level 3 RESTful API is that it's easier to evolve. Exactly because URL templates are not part of the contract, you can decide to change the URL structure when evolving your API.

Imagine that the first version of your API has an (internal) URL template like /orders/{customerId}, so that the example URL http://foo.ploeh.dk/orders/1234 is the address of the order history for customer 1234. However, in version 2 of your API, you realize that this way of thinking is still too RPC-like, and you'd rather prefer /customers/{customerId}/order, e.g. http://foo.ploeh.dk/customers/1234/orders.

With a level 3 RESTful API, you can change your internal URL templates, and as long as you keep providing links, clients following links will not notice the difference. However, if clients are 'hacking' your URLs, their applications may stop working if you change URL templates.

Keep clients safe

In the end, HATEOAS is about encapsulation: make it easy for the client to do the right thing, and make it hard for the client to do the wrong thing. Following links will make clients more robust, because they will be able to handle changes in the API. Making it easy for clients to follow links is one side of designing a good API, but the other side is important too: make it difficult for clients to not follow links: make it difficult for clients to 'hack' URLs.

The services I've helped design so far are level 3 APIs, but they still used hackable URLs. One reason for that was that this is the default in the implementation platform we used (ASP.NET Web API); another reason was that I thought it would be easier for me and the rest of the development team if the URLs were human-readable. Today, I think this decision was a mistake.

What's the harm of supplying human-readable URLs for a level 3 RESTful API? After all, if a client only follows links, the values of the URLs shouldn't matter.

Indeed, if the client only follows links. However, clients are created by human developers, and humans often take the road of least resistance. While there are long-time benefits (robustness) from following links, it is more work in the short term. The API team and I repeatedly experienced that the developers consuming our APIs had 'hacked' our URLs; when we changed our URL templates, their clients broke and they complained. Even though we had tried to explicitly tell them that they must follow links, they didn't. While we never documented our URL templates, they were simply too easy to guess from pure extrapolation.

Opaque URLs

In the future, I plan to make URLs opaque when building level 3 APIs. Instead of http://foo.ploeh.dk/customers/1234/orders, I'm going to make it http://foo.ploeh.dk/DC884298C70C41798ABE9052DC69CAEE, and instead of http://foo.ploeh.dk/products/2345, I'm going to make it http://foo.ploeh.dk/598CB0CAC30646E1BB768596BFE91F2C, and so on.

Obviously, that means that my API will have to maintain some sort of two-way lookup table that can map DC884298C70C41798ABE9052DC69CAEE to a request for customer 1234's orders, 598CB0CAC30646E1BB768596BFE91F2C to request for product 2345, but that's trivial to implement.

It puts a small burden on the server(s), but effectively stops client developers from shooting themselves in their feet.

Summary

Hackable URLs are a good idea if you are building a web site, or a level 1 or 2 REST API, but unless you know that all client developers are enthusiastic RESTafarians, consider producing opaque URLs for level 3 REST APIs.


Comments

I understand the benefits of the idea that clients must follow links, I just can't see how it would work in reality. Say you are building a website to display products and you are consuming a rest API, how do you support deep linking to individual products? OK, you can either use the same URL as the underlying API or encode it in, but what are you expected to do if the API changes it's links? Redirect the user to your home page? What if you want to display information from 2 or more resources on the same webpage?

2013-05-15 8:04 UTC

First of all, keep in mind that while it can be helpful to think about REST design principles in terms of "how would I design this if it was a web site", a REST API is not a web site.

You can do deep linking in your web site, but why would you want to do 'deep linking' for an API? These are two different concerns.

It's very common to create a web site where each page calls many individual services. This can be done either from the web server (e.g. from ASP.NET or similar), or from the browser as AJAX calls. This is commonly known as mash-up architecture, because the GUI is really just a mash-up of service data. Amazon.com works that way. You can still deep link to a web page; it's the web page's responsibility to figure out which services to call with what parameters.

That said, as described in the RESTful Web Services Cookbook, you should serve cool URLs, so if you ever decide to change your internal URI template, you should leave a 301 (Moved Permanently) behind at the old URL. This would enable a client that once bookmarked a resource to follow the redirect to the new address.

2013-05-15 9:11 UTC

When you say "it's the web page's responsibility to figure out which services to call", that's what I'm getting at, how would the client do that?

I'm guessing one way would be to cache links followed and update them on 301s or "re-follow" on 404s. So, say you wanted a web page displaying "product/24", you might:

  • GET the REST endpoint
  • GET product catalogue URL from response
  • GET product URL from response
  • Cache the product URL against the website product URL
  • Subsequent requests hit the cached URL

Then if the product URL changes, if the response is 301, you simply update the cache. If the response is 404 then you'd redo the above steps.

I'm just thinking this through, is the above a "standard" approach for creating rest clients?

2013-05-15 12:04 UTC

That sounds like one way of doing it. I don't think there's a 'standard' way for creating RESTful clients.

The sketch you paint sounds like a lot of work, and it seems that it would be easier if the client could simply assemble appropriate URLs from templates. That would indicate level 1 and 2 REST APIs, which might be perfectly fine if the only purpose of building the service is to support a GUI. However, what you get in exchange for the extra effort it takes to consume a level 3 API, is better decoupling. It's always going to be a trade-off.

It's definitely going to be more work to build and consume a truly HATEOAS-based API, so you should only do it if it's going to provide a good return on investment. When would that be? One general scenario I can think of is when you're building a service, which is going to be consumed by multiple (unknown) clients. If you control the service, but not the clients, I'd say a level 3 API is very beneficial, because it enables you to evolve the API independently of the clients. Conversely, if the only purpose of building a service is to support a single client, it's probably going to be overkill.

2013-05-15 15:15 UTC

Herding Code podcast

Wednesday, 01 May 2013 10:26:00 GMT

I'm on Herding Code.

At the Danish Developer Conference 2013 I had the pleasure of meeting Jon Galloway, who interviewed me for a Herding Code podcast. In this interview, we talk about AutoFixture, testing (or not testing) trivial code, as well as lots of the unit testing topics I also cover in my Pluralsight course on Advanced Unit Testing.


REST lesson learned: Avoid 204 responses

Tuesday, 30 April 2013 10:46:00 GMT

Avoid 204 responses if you're building a HATEOAS application.

This is a lesson about REST API design that I learned while building non-trivial REST APIs. In order to be as supportive of the client as possible, a REST API should not return 204 (No Content) responses.

From the service's perspective, a 204 (No Content) response may be a perfectly valid response to a POST, PUT or DELETE request. Particularly, for a DELETE request it seems very appropriate, because what else can you say?

However, from the perspective of a proper HATEOAS-aware client, a 204 response is problematic because there are no links to follow. When hypermedia acts as the engine of application state, when there are no links, there's no state. In other words, a 204 response throws away all application state.

If a client encounters a 204 response, it can either give up, go to the entry point of the API, or go back to the previous resource it visited. Neither option is particularly good.

Giving up is not a good option if there's still work to do. Essentially, this is equivalent to a crashing client.

Going to the entry point of the API may allow the client to move on, doing what it was doing, but state may still be lost.

Going back (the equivalent of using your browser's back button) may be the best option, but has a couple of problems: First, if the client just did a DELETE, the previous resource in the history may now be gone (it was just deleted). The client would have to go back twice to arrive at a proper resource. Second, while your browser has built-in history, a programmatic HTTP client probably hasn't. You could add that feature to your client, but it would require more work. Once more, it would require the client to maintain state, which means that you'd be moving state from hypermedia to the client. It's just not a HATEOAS-compliant approach.

A good REST API should make it easy to be a client. While this is only a variation of Postel's law, I also like to think of this in terms or courtesy. The service has a lot of information available to it, so it might as well be courteous and help the client by sharing appropriate pieces of information.

Instead of a 204 (No Content) response, tell the client what it can do now.

Responding to POST requests

An HTTP POST request often represents some sort of Command - that is: an intent to produce side effects. If the service handles the request synchronously, it should return the result of invoking the Command.

A common POST action is to create a new resource. At the very least, the REST API should return a 201 (Created) with a proper Location header. That's all described in detail in the RESTful Web Services Cookbook.

Even if the API decides to handle the request asynchronously, it can provide one or more links. Actually, the specification of 202 (Accepted) says that the "entity returned with this response SHOULD include an indication of the request's current status and [...] a pointer to a status monitor". That sounds like a link to me.

Responding to PUT requests

An HTTP PUT request is often intended to update the state of a particular resource. Instead of returning 204 (No Content), the API should be courteous and return the new state of the resource. You may think that this is redundant because the client just transferred the state of the resource to the service, so why pay transmission costs to get the resource back from the service?

Even if a client PUTs the entire state of a resource to the API, the API may still have information about the resource not available to the client. The most universal example is probably the resource's ETag. If the client wishes to do further processing on the resource, it's likely going to need the ETag value sooner or later.

The representation of the resource may also include optional or denormalized data that the client may not have. If that data is optional, the client can make a PUT without it, but it might want that data to display to a user, so it would have to read the latest state of the resource regardless.

Responding to DELETE requests

A DELETE request represents the intent to delete a resource. Thus, if the service successfully handles a DELETE request, what else can it do than returning a 204 (No Content)? After all, the resource has just been removed.

A resource is often a member of a collection, or otherwise 'owned' by a container. As an example, http://foo.ploeh.dk/api/tags/rock represents a "rock" tag, but another way of looking at it is that the /rock resource is contained within the tags container (which is itself a resource). This should be familiar to Atom Pub users.

Imagine that you want to delete the http://foo.ploeh.dk/api/tags/rock resource. In order to accomplish that goal, you issue a DELETE request against it. If all your client gets back is a 204 (No Content), it's just lost its context. Where does it go from there? Unless you keep state on the client, you don't know where you came from.

Instead of returning 204 (No Content), the API should be helpful and suggest places to go. In this example I think one obvious link to provide is to http://foo.ploeh.dk/api/tags - the container from which the client just deleted a resource. Perhaps the client wishes to delete more resources, so that would be a helpful link.

Responding to GET requests

Returning 204 (No Content) as a response to a GET request is a bit weird, but probably not completely unheard of. Such a resource would represent a flag or semaphore, where the existence of the resource (as opposed to a 404 (Not Found)) signifies something. However, consider being helpful to the client and at least provide a link it can use to move on.

Summary

Technically, responding with 204 (No Content) is perfectly valid, but if a REST API does that, its clients lose their current context. A service should be helpful and inform clients where they can go from the current resource. This will make client development easier, and only puts a small burden on the service.


REST lesson learned: Avoid user supplied data in URI segments

Monday, 29 April 2013 12:07:00 GMT

Be careful with user-supplied data in URI segments.

This is a lesson about design of REST APIs that I learned the hard way: if you built service URIs from dynamic data, be careful with the data you allow in URI segments.

Some HTTP frameworks (e.g. the ASP.NET Web API) let you define URI templates or routes that handle incoming requests - e.g.

routeTemplate: "api/{controller}/{id}"

which means that a request to http://foo.ploeh.dk/api/fnaah/sgryt would map the value "fnaah" to controller, and the value "sgryt" to id.

If you are building a level 2 or less service, you may even publish your URI templates to consumers. If you are building a level 3 RESTful API, you may not be publishing your URI templates, but you can still use them internally.

Avoid user-supplied data in URI segments.

Be careful with the source of data you allow to populate URI segments of your URI template. At one point, I was involved with designing a REST API that (among other things) included a 'tag cloud' feature. If you wanted to see the contents of a specific tag, you could navigate to it.

Tags were all user-defined strings, and they had no internal ID, so our first attempt was to simply treat the value of the tag as the ID. That seemed reasonable, because we wanted the tag resource to list all the resources with that particular tag value. Thus, we modeled the URI template for tag resources like the above route template.

That worked well for a URL like http://foo.ploeh.dk/api/tags/rock because it would simply match the value "rock" to the id variable, and we could then list all resources tagged with "rock".

However, some user had defined a tag with the value of "sticky & sweet" (notice the ampersand character), which meant that when you wanted to see all resources with this link, you would have to navigate to http://foo.ploeh.dk/api/tags/sticky & sweet. However, that sort of URL is considered dangerous, and IIS will (by default) refuse to handle it.

Can you get around this by URL encoding the value? No, it's part of the request path, not part of any query string, so that's not going to work. The issue isn't that the URL is invalid, but that the server considers it to be dangerous. Even if you URL encode it, the server will decode it before handling it, and that would you leave you at square one.

You can either change the URI template so that the URL instead becomes http://foo.ploeh.dk/api/tags?id=sticky%20%26%20sweet. This URL encodes the query string (the part of the URL that comes after the ?), but gives you an ugly URL.

Another alternative is to be very strict about input validation, and only allow users to create values that are safe when used as URI segments. However, that's putting an unreasonable technical limitation on an application feature. If a user wants to tag a resource with "sticky & sweet", the service should allow it.

In the end, we used a third alternative: we assigned an internal ID to all tags and mapped back and forth so that the URL for the "sticky & sweet" tag became http://foo.ploeh.dk/api/tags/1234. Yes: that makes it impossible to guess the URL, but we were building a level 3 RESTful API, so clients are expected to follow links - not guess the URL.


REST lessons learned

Monday, 29 April 2013 00:00:00 PDT

This post provides an overview of some lessons I learned while bulding non-trivial REST APIs.

Last year I spent a good deal of the year designing and implementing a handful of non-trivial REST APIs for a customer of mine. During that process, I learned some small lessons about the design of RESTful systems that I haven't seen described elsewhere, and I want to share these lessons with you.

In order to learn the concepts and philosphy behind REST, I think that REST in Practice is a great resource (pun intended), but when it comes to practical guidance, I find the RESTful Web Services Cookbook invaluable. It's full of useful and concrete tips and tricks for building RESTful APIs, but I don't remember reading about the following lessons, that I had to learn the hard way.

There's so much hype and misrepresentation about REST that I have to point out that when I'm talking about REST, I mean full-on, level 3 REST, with resources, verbs, hypermedia controls and the works.

Each of these lessons deserves a small article of its own, but here's an overview:

I hope you find these tips useful.


How to change the Generate Property Stub refactoring code snippet in Visual Studio 2012

Tuesday, 23 April 2013 14:00:00 GMT

This post describes how to change the code template for the "Generate Property Stub" refactoring in Visual Studio 2012.

This is mostly a quick note for my own benefit (since I just spent half an hour chasing this down), but I'm posting it here on the blog, in case someone else might find it useful. Here's how to change the code snippet for the "Generate Property Stub" smart tag in Visual Studio 2012:

The template for this code is defined in

C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC#\Snippets\1033
\Refactoring\GenerateProperty - Auto Property.snippet

You can open this file and hand-edit it, but contrary to previous versions of Visual Studio, it seems that you have to restart Visual Studio before the change takes effect.

If you do this, be sure that you know what you're doing; you'd be mucking around with the internals of your Visual Studio installation.


Advanced Unit Testing Pluralsight course

Monday, 15 April 2013 18:29:00 GMT

Service announcement: my new Pluralsight course Advanced Unit Testing is now available. Read more in Pluralsight's announcement, or go straight to the course.

If you don't already have a Pluralsight account, you can get a free trial of up to 200 minutes.


FizzBuzz kata in Clojure

Thursday, 11 April 2013 06:20:00 GMT

This post describes my first experience with doing the FizzBuzz kata in Clojure.

After having looked at Clojure for some time, I finally had a bit of time to play with it, so I decided to do the FizzBuzz kata in Clojure.

Single fizzbuzz function

Clojure has a built-in testing framework, so I wrote a Parameterized Test for a single fizzbuzz function. Using TDD, I added test cases a little at a time, attempting to follow the Transformation Priority Premise, but the end result is this:

(ns fizzbuzz.core-test
  (:use clojure.test
        fizzbuzz.core))

(deftest fizzbuzz-test
  (are [expected i] (= expected (fizzbuzz i))
       "1" 1
       "2" 2
       "Fizz" 3
       "4" 4
       "Buzz" 5
       "Fizz" 6
       "7" 7
       "8" 8
       "Fizz" 9
       "Buzz" 10
       "11" 11
       "Fizz" 12
       "13" 13
       "14" 14
       "FizzBuzz" 15
       "FizzBuzz" 30))

Clojure syntax is somewhat backwards from what I'm normally used to, but the are macro expands into a collection of tests that each evaluate whether the result of invoking the fizzbuzz function with i is equal to expected.

Through a series of transformations of the SUT, I ended up with this implementation of the fizzbuzz function:

(ns fizzbuzz.core)

(defn fizzbuzz [i]
  (cond
    (= 0 (mod i 15)) "FizzBuzz"
    (= 0 (mod i 3)) "Fizz"
    (= 0 (mod i 5)) "Buzz"
    :else (str i)))

This defines a function called fizzbuzz taking a single argument i. The cond macro evaluates each test and returns the expression associated with the first test that evauluates to true. The first test checks if i is divisible with 15 and returns "FizzBuzz" if this is the case; the next test checks if i is divisible with 3 and returns "Fizz" if this is true, and so on.

Printing a range of fizzbuzz values

The task defined by the kata is to print all FizzBuzz values from 1 to 100, so the above function is't the final solution. The next step I took was to write a test that defines a version of the fizzbuzz function taking two parameters:

(def acceptance-expected
"1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz
Fizz
22
23
Fizz
Buzz
26
Fizz
28
29
FizzBuzz
31
32
Fizz
34
Buzz
Fizz
37
38
Fizz
Buzz
41
Fizz
43
44
FizzBuzz
46
47
Fizz
49
Buzz
Fizz
52
53
Fizz
Buzz
56
Fizz
58
59
FizzBuzz
61
62
Fizz
64
Buzz
Fizz
67
68
Fizz
Buzz
71
Fizz
73
74
FizzBuzz
76
77
Fizz
79
Buzz
Fizz
82
83
Fizz
Buzz
86
Fizz
88
89
FizzBuzz
91
92
Fizz
94
Buzz
Fizz
97
98
Fizz
Buzz")

(deftest acceptance-test
  (is (= acceptance-expected (fizzbuzz 1 101))))

I decided to define the acceptance-expected value outside of the test case itself, as I thought that made the test a bit more readable. The test case is defined by the is macro and states that the expected value is acceptance-expected and the actual value is the result of invoking the fizzbuzz function with two arguments: 1 as the (inclusive) start value, and 101 as the (exclusive) end value. The above code listing of fizzbuzz only accept one argument, but the new test case requires two arguments, so I added an overload to the function:

(defn fizzbuzz
  ([i]
    (cond
      (= 0 (mod i 15)) "FizzBuzz"
      (= 0 (mod i 3)) "Fizz"
      (= 0 (mod i 5)) "Buzz"
      :else (str i)))
  ([start end]
    (apply str
      (interpose "\n"
        (map fizzbuzz (range start end))))))

The previous implementation is still there, now contained within the overload taking a single argument i, but now there's also a new overload taking two arguments: start and end.

This overload generates a sequence of integers from start to end using the range function. It then maps that sequence of integers into a sequence of strings by mapping each integer to a string with the fizzbuzz function. That gives you a sequence of strings such as ("1" "2" "Fizz" "4" "Buzz").

In order to print all the FizzBuzz strings, I need to interpose a newline character between each string, which produces a new sequence of strings such as ("1" "\n" "2" "\n" "Fizz" "\n" "4" "\n" "Buzz"). To concatenate all these strings, I apply the str function to the sequence.

Printing FizzBuzz values from 1 to 100

The requirements of the kata is to print all FizzBuzz values from 1 to 100, and the code already does this. However, I interpret the kata as requiring a single function that takes no parameters, so I added an acceptance test case:

(deftest acceptance-test
  (is (= acceptance-expected (fizzbuzz 1 101)))
  (is (= acceptance-expected (fizzbuzz))))

Notice the second test case in the last line of code that invokes the fizzbuzz function without any parameters. It's easily resolved by adding a third overload:

(defn fizzbuzz
  ([]
    (fizzbuzz 1 101))
  ([i]
    (cond
      (= 0 (mod i 15)) "FizzBuzz"
      (= 0 (mod i 3)) "Fizz"
      (= 0 (mod i 5)) "Buzz"
      :else (str i)))
  ([start end]
    (apply str
      (interpose "\n"
        (map fizzbuzz (range start end))))))

As you can see, the first overload takes no parameters and simply invokes the previously desribed overload with the start and end arguments.

FWIW, this entire solution is structurally similar to my implementation of FizzBuzz in F#.


Page 1 of 23