Chris James - Software Engineer and other things

Go back to basics with MVC

25 July 2019

This is another follow up post to an episode of Go time I listened to the other day which seems to get my creative juices flowing.

It talked about web development in Go and a few things stuck out to me

A cautionary tale of generating HTML

Early on in the episode it was described how the built-in template library wasn't very expressive and described that the Go Buffalo framework uses Plush

Powerful, flexible, and extendable, Plush is there to make writing your templates that much easier.

Enter Jade

8ish years ago I worked on a pretty major project which was written in Scala. We decided to use the Jade template engine to generate our HTML which, like Plush allows you to do a lot of clever coding inside your templates.

This felt amazing at first but quickly became a nightmare. With the flexibility Jade gave us we made a bit of a mess. Our templates became ugly, hard to understand and difficult to test as more and more business logic leaked into our templates

In software I often see flexibility as rope that a team can hang itself with and I tend to prefer very opinionated and constrained things (like Go!).

What did we learn?

Software engineers are doomed to relearn things. We had forgotten about separation of concerns, in particular the guidance the Model-View-Controller design pattern prescribes.

MVC

It's quite surprising how badly people misunderstand MVC. If you go on Reddit or Twitter you will hear people telling other developers that "MVC isn't suited to Go".

Often people seem to think MVC is about folder structures, class names, or other fairly superficial concerns. So when developers from say a Rails background show their Go code with an array of folders they get derided and thrown to the lions.

MVC does not prescribe folder structure and I worry a lot of people seem to miss the point of the principle.

Model–View–Controller (usually known as MVC) is an architectural pattern commonly used for developing user interfaces that divides an application into three interconnected parts.

Controller

Intercepts "requests" (such as HTTP), parses them and then calls the appropriate "Model", getting some kind of data as a result and then sends it to a "View". In the Go world, that's usually a http.Handler

Model

Loosely, it's where your domain logic lives. I personally think it's quite poorly named but the important thing is the separation of concern. It must be decoupled from the Controller and View, so if you see anything HTTP related passed through from the controller it's likely you're violating that decoupling. They should also know nothing about views.

View

It doesn't know anything about controllers or models. It should probably get passed to it some kind of ViewModel which is just a collection of data it needs to render a view. A well designed view is simple and doesnt have domain logic; because templates are very hard to test cheaply.

What do these principles buy you?

Your domain code is cleanly separated from the rest of the system so it is easy to test and can be "plugged in" to different uses beyond your web server; for example a CLI tool, or just as a package for others to use.

It is trivial to create and edit views as they are merely mappings from a bundle of generic data into HTML (or whatever). It also makes them more accessible for frontend developers et al.

Your controller has clear concerns and are also easy to test.

Circling back to the start, I would be very cautious about something that allows me to write very expressive code for my view. These kind of tools trick developers with promises of convenience and power but often lead to leaky abstractions unless you are very disciplined.

Now have a think about all the tutorials you may have read around HTTP servers and Go. The good ones will recommended all of these principles without perhaps just calling them out. Rather than the community dancing around the idea of MVC, just embrace it. It's a tried and tested pattern and most people advocate it anyway, just not explicitly. If we were explicit then maybe there wouldn't be a new post every week about how to structure a HTTP server!

How to structure your web app

  1. Understand the idea of MVC.
  2. Your http.Handlers are your controllers, make sure they only do things controllers do according to above.
  3. All your other business logic (Model) lives elsewhere, in packages centered around real things. If it's a bank i'd maybe have packages for Account, Currency, Customer e.t.c. Your controller will call things in your packages to do useful stuff.
  4. Depending on what you're building you can just use the encoding packages to spit out XML or JSON, or if it's HTML just use template/html.

Functional programming and Go

10 June 2019

The Go Time podcast on functional programming (fp) was an interesting listen and as someone who has done many years of both Scala and Go it's a topic I'm interested in.

The show talked about the stuff you'd expect, pure functions, immutability, higher-order functions etc. Here's some thoughts I'd like to add on top.

Errors

Let's have a look at a function written in a few programming languages

Go

func Divide(x, y int) (int, error)

Scala

def Divide(x: Int, y: Int): Either[Exception, Int]

Java

public Int Divide(int x, int y)

What does the Go and Scala one have in common that Java does not?

You can trust the function from the signature. You do not need to read the function body to understand that it can fail.

In Java it would not be surprising for the function to throw an exception. Unless the author decided to use checked exceptions it means the user has to inspect the function body to see if anything potentially bad can happen.

At first throwing exceptions can seem convenient but in the long run become a pain.

FP values explicitness and and so does Go. It is recommended that you do not write packages that panic because that makes your code more difficult to work with and trust.

Errors as values

Go (and Scala) treat errors as values just like any other data in our system.

Crucially there is no special special syntax to work with errors. Error is just an interface that everyone happens to use as it's built-in.

Go 2 has proposals to make working with errors more convenient because error handling in Go right now can be fairly verbose.

a, err := Divide(2, 0)

if err != nil {
    return 0, fmt.Errorf("oh no %v", err)
}

b, err := Divide(2, 2)

if err != nil {
    return 0, fmt.Errorf("oh no %v", err)
}

return a + b, nil

For Go 2, we would like to make error checks more lightweight, reducing the amount of Go program text dedicated to error checking. We also want to make it more convenient to write error handling, raising the likelihood that programmers will take the time to do it.

The main problem with errors in Go is they do not safely compose and this is an important property (and advantage) of FP

Let's see how Scala let's us safely compose functions that can sometimes fail

val result: Either[Exception, Int] = for {
  a <- Divide(2, 0)
  b <- Divide(2, 2)
} yield(a + b)

Explanation

I need to stress this isn't special syntax, the for syntax works with any type that has map and flatMap. (cough monads cough)

Go 2, errors and generics

I worry about the proposals around errors in Go 2 because they are making errors "special". It is adding ad-hoc syntax and that doesn't feel very "Go like" to me.

Go 2 also has generics under consideration but the two topics seem to be discussed in separation a lot of the time. FP has identified patterns and datatypes for safely and easily working with errors; and they almost all rely on generics.

Instead of

type Reader interface {
        Read(p []byte) (n int, err error)
}

Maybe we could have

type Reader interface {
        Read(p []byte) Either<Int, error>
}

With a good generics solution we wouldn't have to have special syntax that only work with errors and errors can retain their non-special status they have right now.

We'd still need some kind of new syntax to help with composition (like the Scala one earlier) but it wouldn't be tied down to a specific datatype; and could be based on proven and existing patterns from other FP languages.

(Incidentally, I find Kotlin suffers from trying too hard to not be like Scala and instead invents syntax and idioms which are not as portable as learning about say.... monads.)

Wrapping up

Go already values a lot of principles from FP. Rather than creating new syntax that makes errors somehow different and special, wouldn't it be better if Go continued to lean on FP principles to help Go programmers work with errors a little more elegantly?

I could be totally off the mark (this is very likely, I am not a language designer). It can definitely be argued that bringing in more elements from FP is also not "Go like".

Generics are simple but not always easy. Adding ad-hoc syntax to work with errors is probably easy but not simple.

Go errors right now are simple and easy. If we dont want to tack on more complexity just for the sake of making code less verbose... maybe dont bother and leave errors as they are?

What's the difference between simple and easy?

Simple made easy is one of my most favourite tech talks.

Rich Hickey emphasizes simplicity’s virtues over easiness’, showing that while many choose easiness they may end up with complexity, and the better way is to choose easiness along the simplicity path.