Blog

how to not know what you're doing: the spike

Written by Thomas Henderson
Published on 23 October 2017

“Write short functions.” “Give functions expressive names.” “Classes and namespaces should have one responsibility.” “Don’t repeat yourself.”

It’s good advice when you know what you’re doing. But what if you don’t? What if you don’t know what commands and queries belong together in functions? What if you haven’t conceptualized the domain well enough to give names and responsibilities to its components, or to recognize duplication?

We can’t encode what we don’t understand. So when we’re faced with a problem we don’t know how to solve, we need to spend effort on understanding it, before we design a solution for it. The name I’ve been taught for code written for understanding, rather than for production, is a ‘spike.’

I read somewhere that there’s no such thing as ‘no design’ — either you think about your design, or you don’t, and the result will be a thoughtful design (hopefully good), or thoughtless design (probably bad). Last week I referred to the idea that designing means pulling things apart so they can be put back together. When we begin with an unfamiliar domain, we don’t know what the good seams in the domain are — where it makes good sense to pull things apart to consider them individually.

I propose that if there’s no such thing as no design, and if pulling things apart thoughtlessly leads to a mess, then our best design in an unfamiliar domain is the one in which we pull things apart as little as possible.

Example: Http Server Spike (Sockets)

Here’s some spike code I wrote in order to try and understand sockets, server sockets, and input/output streams.

public class Service {
    public static void main(String[] args) throws Exception {
        int port = 1337;
        String directory = null;

        System.out.println("Processing command line arguments");
        for (int i=0; i < args.length; i++) {
            String token = args[i];
            if (Objects.equals(token, "-p")) {
                port = Integer.parseInt(args[i+1]);
            } else if (token.equals("-d")) {
                directory = args[i+1];
            }
        }

        System.out.println("Listening on " + port);
        System.out.println("Serving resources at " + directory);
        ServerSocket listener = new ServerSocket(port);

        while(true) {
            System.out.println("Accepting connections");
            Socket io = listener.accept();

            System.out.println("\nConnected");
            BufferedReader reading = new BufferedReader(new InputStreamReader(io.getInputStream()));
            PrintWriter writing = new PrintWriter(io.getOutputStream(), true);

            System.out.println("Request:");
            String line = reading.readLine();
            do {
                System.out.println("I heard: " + line);
                line = reading.readLine();
            } while (!line.isEmpty());

            System.out.println("\nResponding with:");

            String requestLine = "HTTP/1.1 200 OK";
            System.out.println(requestLine);
            writing.println(requestLine);

            String contentType = "Content-Type: text/html";
            System.out.println(contentType);
            writing.println(contentType);

            String contentLength = "Content-Length: 0";
            System.out.println(contentLength);
            writing.println(contentLength);

            System.out.println("<CRLF>");
            writing.println("");

            System.out.println("\nClosing connection\n");
        }
    }
}

I’d start the server, and then in another process act as a client, running

$ curl localhost:1337 -v

to hit my server with a basic GET request and be verbose about what I got. On the client side, the output of this command looked like

Rebuilt URL to: localhost:1337/
  Trying ::1...
TCP_NODELAY set
Connected to localhost (::1) port 1337 (#0)
> GET / HTTP/1.1
> Host: localhost:1337
> User-Agent: curl/7.56.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Content-Type: text/html
< Content-Length: 0
< 
Connection #0 to host localhost left intact

And on the server side, all those System.out.println s gave this:

Processing command line arguments
Listening on 1337
Serving resources at null
Accepting connections

Connected
Request:
I heard: GET / HTTP/1.1
I heard: Host: localhost:1337
I heard: User-Agent: curl/7.56.0
I heard: Accept: */*

Responding with:
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 0
<CRLF>

Closing connection

Accepting connections

Everything bad is good for you

Many things I discovered to be helpful in this spike are things that you wouldn’t want in your production code.

Questions