
Go kit: Go in the modern enterprise - sagichmal
http://peter.bourgon.org/go-kit
======
krenoten
I'd be pretty nervous about any attempt to recreate Finagle in Go. Scala
provides many ways of creating composable complexity. Finagle leverages pretty
much every feature of Scala (and there are many) to maximize this
composability in a sane way, but because there is simply so much complexity in
Finagle, a lot of opportunities for composability have been skipped due to
lack of need within Twitter (maybe finagle-http has significantly improved in
the last year, which is mostly what I'm thinking about when I say this) but
it's a LOT of complexity, sort of like the RoR of distributed systems, and
when you want to diverge from Twitter's chosen trade-offs, it may be a hellish
experience. It's pretty sweet for when your problem space aligns with
Twitter's, however.

Go has basically none of the mechanisms for sanely creating composable
complexity that Scala has. It makes me really grossed out to think about
something with the complexity that Finagle exposes as a Go library. Go is all
about small, sturdy tools that work for most of the cases most of the time and
that compose well with each other - IF - they are built into the standard
library. And if it's not, have fun spending your time writing uni-typed
interfaces for any kind of interop with desired complexity. Go is great for
writing dead simple byte slingers, pipelines that will be rigid in the most
dynamic possibility, guns that shoot JS and interact with services, etc.
That's simply not Finagle.

~~~
jbooth
Could you provide examples (beyond generics) where 'composable complexity'
helped finagle achieve a goal?

My experience with Scala has been that an author's 'composable complexity'
becomes a reader's 'wtf is this complexity'.

Go's philosophy has been to avoid complexity altogether in most of the tools
they give you, forcing you to write lowest-common-denominator code that anyone
can easily understand. Generics are sorely missed, but I really don't see
additional expressive power from most scala features, just additional things I
need to keep in my head for an occasional 5-line savings in the middle of some
method (which IMO doesn't change the complexity equation at all). Would love
to see some beautiful examples that change my mind.

~~~
Zikes
Go libraries are typically highly composable because they operate mostly on
primitives or strictly defined common interfaces. Orthogonality is a key
feature.

~~~
codygman
If I recall correctly those libraries aren't type safe _and_ composable, but
perhaps I'm remembering wrong.

~~~
Zikes
They are type safe, except for the unsafe package but it's hard not to know
what you're getting into with that one.

One of the best examples of composability is the Reader and Writer interface.
They're designed to operate on slices of bytes, so a package intended to read
from a Reader can read from files, stdin, networks, sockets, and a myriad
other systems with absolutely no changes to the package's code or its
implementation.

~~~
codygman
How would you do decode this json in a type safe way using Go?

    
    
        {"purchaseType": "Rent","price": 0.99,"title": "inception"}
    

Here is an implementation in Haskell:

    
    
        {-# LANGUAGE DeriveGeneric #-}
        {-# LANGUAGE OverloadedStrings #-}
        
        import Data.Aeson
        import GHC.Generics
        import qualified Data.ByteString.Lazy as BL
        
        data PurchaseType = Free | Rent | Buy | Subscriber deriving (Show, Generic)
        instance FromJSON PurchaseType
        instance ToJSON PurchaseType
        
        data Media = Media { title :: String, price :: Float, purchaseType :: PurchaseType} deriving (Show, Generic)
        
        instance FromJSON Media
        instance ToJSON Media
        
        jsonData = "{\"title\": \"inception\", \"price\": 0.99, \"purchaseType\": \"Rent\"}"
       
        main = print $ (decode jsonData :: Maybe Media)
        -- running main gets:
        -- λ> main
        -- Just (Media {title = "inception", price = 0.99, purchaseType = Rent})
    

EDIT: Spec Change, boss wants an order confirmation message!

    
    
        orderConfirmationMessage (Media mediaTitle _ Rent) = putStrLn ("Thanks for renting " ++ mediaTitle)
    

Then we recompile and... what's this?

    
    
        Main.hs:19:1-100: Warning: …
            Pattern match(es) are non-exhaustive
            In an equation for ‘orderConfirmationMessage’:
                Patterns not matched:
                    Media _ _ Free
                    Media _ _ Buy
                    Media _ _ Subscriber
    

Good thing GHC had told us Free/Buy/Subscriber's needed messages instead of
our boss! In fact, had I been using -Wall this would be a compile time error.

Getting to the point, my real questions are:

1\. What is Go's way of ensuring your function handles cases like this, or is
there an idiomatic way of avoiding it?

2\. How do you discern between MovieType's without resorting to just using
Strings and compromising type safety?

~~~
Zikes
How about an array of Media?

    
    
        package main
    
        import (
            "encoding/json"
            "fmt"
        )
    
        type Media struct {
            Title        string
            Price        float64
            PurchaseType string
        }
    
        func main() {
            jsonData := []byte(`
                [
                    {"purchaseType":"Rent","price":0.99,"title":"Inception"},
                    {"purchaseType":"Free","price":0.00,"title":"Johnny Mnemonic"},
                    {"purchaseType":"Buy","price":17.99,"title":"John Wick"}
                ]
            `)
            var media []Media
            err := json.Unmarshal(jsonData, &media)
            if err != nil {
                fmt.Println("error: ", err)
            }
            fmt.Printf("%+v\n", media)
        }
    

Output:

    
    
        $ go run main.go
        [{Title:Inception Price:0.99 PurchaseType:Rent}
        {Title:Johnny Mnemonic Price:0 PurchaseType:Free}
        {Title:John Wick Price:17.99 PurchaseType:Buy}]
    

Edit: Regarding the MediaType:
[http://stackoverflow.com/questions/14426366/what-is-an-
idiom...](http://stackoverflow.com/questions/14426366/what-is-an-idiomatic-
way-of-representing-enums-in-golang)

~~~
sagichmal
PurchaseType doesn't need to be stringly typed. See
[https://talks.golang.org/2015/json.slide#1](https://talks.golang.org/2015/json.slide#1).

    
    
        type PurchaseType byte
        
        const (
            Free PurchaseType = iota
            Rent
            Buy
        )
        
        func (t *PurchaseType) UnmarshalJSON(data []byte) error {
            var s string
            if err := json.Unmarshal(data, &s); err != nil {
                return fmt.Errorf("purchase type should be a string, got %s", data)
            }
            have, ok := map[string]PurchaseType{"Free": Free, "Rent": Rent, "Buy": Buy}[s]
            if !ok {
                return fmt.Errorf("invalid purchase type %q", s)
            }
            *t = have
            return nil
        }
        
    

That's enough to give this output:

    
    
        [{Inception 0.99 Rent} {Johnny Mnemonic 0 Free} {John Wick 17.99 Buy}]
    

[http://play.golang.org/p/dyNVlgmFtu](http://play.golang.org/p/dyNVlgmFtu)

------
tekacs
This is well written.

I gave a talk at the (unaffiliated) Hacker News London Meetup on composing a
microservice architecture and why I think a framework to bind today's
disparate components needs to exist:
[http://vimeo.com/118895501](http://vimeo.com/118895501)

I don't so much believe that any language is perfectly poised to implement
this, however - we're working on a framework[1] that focuses on making every
language a first-class participant.

Furthermore, it's self-hosting, with most of the core itself implemented as
communicating microservices.

It's possible to reimplement bits of the core in different languages!

We've been waiting to finish a polished implementation before releasing
anything (and talking to individual companies about their needs), but I'm
starting to think that's a bad idea. :)

[1]: [http://wym.io/](http://wym.io/)

~~~
aikah
What's the difference between a spec and a "multi-language" framework? I mean
there is none. You can't have a multi language framework without a strict
interface therefore a spec. Now what you provide seems to be the actual
framework and just not a spec,which is great, but how is it different from
Rest or SOAP, in essence?

Also,since languages are vastly different(in features), don't you think it
will limit what your framework can do?

Great talk anyway.

I believe however,that having a central authority that orchestrates services
is better than micro services that can talk directly to each other.In my
opinion,some kind of mediator is mandatory,one that registers services and
manage service communication.

~~~
tekacs
Addressing each of your points in turn:

\- What's the difference from a spec?

There used to be a FAQ on our site emphasising that at its core _wym_ is a
spec. Our reference implementation, itself made up of multiple pieces (wymd,
wymsy, oneweb, ...) provides a way to actually make use of that spec and to
validate all the guarantees of the strict interfaces.

Also, I'm not a fan of entities who would define a spec in isolation without a
reference implementation alongside it - where's the proof that you're making
the right decisions? We provide that implementation to keep ourselves
grounded.

\- Won't multi-language limit features?

Not really.

For one thing, since we're self-hosting we can't afford to lack power and
expressivity. For example everyone has the ability to refer to types (as per
generics) else we wouldn't be able to implement the core. ;)

Our interface language - it's quite simple, to make the addition of new
language bindings trivial, but we expose built-in services to use more
powerful features simply by switching which nodes you're holding on to -
there's a data-implementation divide, where a node needn't understand a
feature in order to deal in its data.

The net effect is that language bindings can be created trivially and evolved
to support features as a good way of dealing with them is found (e.g. in the
simplest case, Javascript Int64 support).

\- Having a central authority is better.

This is an interesting one :D - I agree, in a way. A wymd cluster self-starts
a consensus using a lock service, performs master election and then uses this
consensus service for registration and discovery.

We have an elected distributed authority. :P

------
jbooth
Any efforts towards a version-numbering standard for dependencies? Godeps is
ok as a workaround, but published versions of things like logging packages,
etc, would make me feel a lot safer when depending on them.

And no, I'd rather not 'vendor' source-snapshots of a logging package into
every project I ever work on.

~~~
threeseed
That's an interesting point. Does Go have an equivalent of Sonatype's Nexus ?

This is basically mandatory in most enterprises since often you can't just go
into the wild to pickup new dependencies. Plus keeping build artefacts managed
is pretty important.

~~~
NateDad
Man, that page needs a "What is Nexus?" link. I can't really tell what it is..
I guess some sort of central repo for packages?

What go has is Godep
([https://github.com/tools/godep](https://github.com/tools/godep)) which with
a single command can copy all your project's dependencies into your repo. from
there on out, the dependencies are just part of source control like anything
else, so you don't have to open up your build machine to the external
internet, for example.

~~~
threeseed
Yes. It is just a central repo that also does caching/proxying of external
repositories. In many enterprises developers won't have access to download
from the external internet so godep won't work.

It also has a fine grained security model and allows for security evaluations
of libraries (check for outdated/insecure etc).

------
knodi
The problem is in enterprise Go is really hard to push currently due to
management no having proper understanding of it. I believe Go is the future
and I recently tried to push Go in using it to write a new semi-real time
system to handle many little messages with complex business logic but nope CTO
went with node.js and have a callback hell.

Sorry didn't mean to rant... Love Go its the future, use it.

~~~
0xdeadbeefbabe
So why is node, with it's callback hell and single thread, worse? Is it
syntax? That single thread assumption can free you from some significant
problems, you know. golang's own http lib is event driven and uses callbacks,
but perhaps less hellishly. I agree it is hard to push go if you don't
properly understand it.

~~~
lebek
Go's http package doesn't use callbacks at the API level. See here
[http://golang.org/pkg/net/http/](http://golang.org/pkg/net/http/)

------
deferpanic
As for metrics - we've been working precisely on that @
[https://deferpanic.com](https://deferpanic.com) . To some extent logging as
well. This means (exactly as OP mentioned) - memory, gc, go-routines, request
durations, db query latency, logging errors/panics, etc.

We are extremely open to suggestions/feedback.

One key point I'd like to point out though, is that OP specifically mentioned
the go kit being targeted towards a company with "100–1000+ engineers". I
think this is highly on point - if you have less engineers than this you
probably don't have enough resources to maintain these sorts of systems
yourselves and this is where we think we can help out.

------
zwieback
_When we read the word enterprise we probably think of older, slow-moving
bureaucracies, like IBM, HP, or even Red Hat. But it’s been a long time since
companies like those have been technical leaders. Decades, in some cases. Now,
companies like Google, Amazon, Twitter, Netflix, Facebook, Spotify, or even
SoundCloud set the tone for our industry. These modern enterprises tend to
share a few characteristics..._

Ouch! I work for HP and while I agree that we're not exactly a nimble, fast
moving startup I think it's not a good comparison. IBM and HP still build a
lot of physical things while the listed "modern" enterprises are primarily SW.
Google would be the exception here but from an engineering point of view
Google uses it's vast war chest to play around with very cool HW but isn't
much concerned with running a business based on that. So, I hope Go has a good
run of it but it's not going to build your next printer.

~~~
enneff
He's talking about software.

------
wiremine
This is a great start. I'm actually in the middle of a writing a Golang
service, and this list checks off everything I've been dealing with.

A lot of these things aren't golang-specific, though. Ideally it would be easy
to swap in services written in different languages, and the conventions stay
the same.

------
bmurphy1976
This is great. I hope something like this is built. Go needs it.

However, not only did we need something like this 5 years ago, we needed it to
be language/platform agnostic then. We need it more so now. We won't be able
to function without it 5 years from now.

The reality is Pandora's box has been opened there is no going back. We have
software we need to run in production at scale that is written in a multitude
of languages. We have our Python/Perl monitoring systems talking to our Java
search APIs talking to our NodeJS frontend APIs talking to our legacy
C#/Java/C++ applications over messaging systems written in
Erlang/Ruby/Java/Go.

Every single one of these platforms need this functionality and everything
needs to run in a cutthroat environment with fewer people, resources, and
money to manage it.

We need performance, discoverability, security, scalability, deployability,
testability, reliability and most importantly EASY. Not enterprisey over-
engineered bullshit. I mean real easy to use plug and play, simple APIs,
minimal JSON/YAML, small focused tools simple.

We need to do it in a way that is portable to every cloud provider so we can
get the best bang for our buck and keep the cloud providers honest and avoid
lock in.

We need to do it in a way that makes the "hardware" vanish. We don't want to
manage hardware anymore. We want to deploy our software and we want to know it
will work and we don't want to compromise functionality (ala Google App Engine
or being forced into the "Tomcat 7" box).

I applaud this line of thinking, but I don't think it goes far enough. In 5
years time, I don't think we'll deploy servers anymore. We'll deploy services
and those services will be in whatever language and those services will find
each other and communicate with each other over the network and will be
reliable, secure, and scalable.

Most importantly, it needs to be open. Truly open. We don't want to trade one
master for another.

That's the software I would like to see. This could be a foundational
component of something like this, but it's only the beginning.

I know about CoreOS/Docker/Mesos/Marathon, etc. Tools like those are an
important piece of this puzzle, but not the only piece. They are a vast
improvement over past initiatives ( _cough_ openstack _cough_ ), but they too
are only a step in the right direction. We still have a lot of work to do to
realize this vision of the future.

~~~
reality_czech
I am old enough that I used CORBA. That basically checked all your boxes...
multi-language, platform agnostic, discoverable, standardized serialization
formats, you name it.

It was also an overly complex piece of shit.

People learn nothing from the past.

~~~
tekacs
I beg to disagree. I just gave a talk[1] about exactly how we[2] went back and
took time to learn lessons from designs like CORBA (we're building another
microservices framework).

Not least I talk about how it's important to build something that modularises
complexity and above all else, focuses first on being _usable by humans_
rather than on technical ascension.

_Some_ of us _are_ listening.

[1]: [https://vimeo.com/118895501](https://vimeo.com/118895501)

[2]: [http://wym.io/](http://wym.io/)

------
reality_czech
Uh oh. The "framework people" found us.

Hopefully they'll be too busy writing
AbstractFactoryBuilderServiceDispatcherProxies to actually do any damage to
production systems...

~~~
enneff
I've talked to the author extensively about these ideas, and let me assure you
he is firmly in the "no monolithic frameworks please" camp. (He's also the
author of some great Go code, btw.)

What he proposes here looks more to me like a standard library for building
distributed systems, which makes a lot of sense to me.

~~~
reality_czech
I don't see the difference between "a standard libary for building systems"
and a framework. Just write libraries for what you need and forget the idea of
some overarching framework.

~~~
enneff
When we built the Go standard library it was valuable for us to think of it as
a whole. What would most of our users need? Are there any gaps? I think the
gokit project is a good opportunity to focus the energies of likeminded people
in the Go community, to close the gaps and work together.

------
qooleot
Hey just curious, why did you pick spacemonkeygo/monitor over mozilla-
services/heka for data collection?

I was thinking of using heka for a logstash-like project and just wanted to
see if you had found several know issues, or that monitor really shined, or
that maybe the feature set you needed just didn't overlap to the point they
were comparable for your needs?

~~~
sagichmal
It wasn't chosen for anything, it was just a related project. And I guess not
for its data collection functionality -- that's orthogonal to the goals of the
toolkit -- but presumably for the instrumentation and request tracing bits.

------
zobzu
Sounds like a package manager with s/glibc/go packages/ and s/otherlibs/go
packages/

Not necessarily bad but.

In fact this is similar to how Go replaces the OS scheduling and message
passing.

Maybe its a necessary path to evolution. Til the OS itself can get fixed
without "it doesnt run X or Z" problems.

------
telecoda
I´m all for in increased uptake of go. But I don want it getting dragged into
Enterprise with a big ¨E¨ and going the way of java with its monolithic
enterprise frameworks.

Somehow we will end up losing the features of the language that make is
currently so endearing.

------
valevk
Golang just needs more database drivers for enterprise products (Informix...),
and some libraries that make office products and the windows enironment
(active direcory...) more assimilable.

~~~
CatDevURandom
I completely agree. Active directory. Better database drivers have blocked me
from using Go on several projects at work.

------
codygman
A related musing about Go I wrote somewhere else before...

Option types as well as lack of support for things like either and generics
are why Go quickly became unappealing to me for real world work.

Check out this haskell example (note that the exceptions are values as well,
so this is more flexible than most languages try or panic in Go):

    
    
        failureExample :: IO (Either SomeException (Response LBS.ByteString))
        failureExample = try $ do
          get "http://www.httpbin.org/status/200"
          get "http://www.httpbin.org/status/200"
          get "http://www.httpbin.org/status/200"
          get "http://www.httpbin.org/status/404"
        
        main = failureExample >>= \case
          Right r -> putStrLn $ "The successful pages status was (spoiler: it's 200!): " ++ show (r ^. responseStatus)
          Left e -> putStrLn ("error: " ++ show e)
        

The equivalent Go is much longer and needlessly verbose:

    
    
        func failureExample()(*http.Response) {
            // 1st get, do nothing if success else print exception and exit
            response, err := http.Get("http://httpbin.org/status/200")
            if err != nil {
                fmt.Printf("%s", err)
                os.Exit(1)
            } else {
                defer response.Body.Close()
            }
        
            // 2nd get, do nothing if success else print exception and exit
            response2, err := http.Get("http://httpbin.org/status/200")
            if err != nil {
                fmt.Printf("%s", err)
                os.Exit(1)
            } else {
                defer response2.Body.Close()
            }
        
        
            // 3rd get, do nothing if success else print exception and exit
            response3, err := http.Get("http://httpbin.org/status/200")
            if err != nil {
                fmt.Printf("%s", err)
                os.Exit(1)
            } else {
                defer response3.Body.Close()
            }
        
        
            // 4th get, return response if success else print exception and exit
            response4, err := http.Get("http://httpbin.org/status/404")
            if err != nil {
                fmt.Printf("%s", err)
                os.Exit(1)
            } else {
                defer response4.Body.Close()
            }
        
            return response4
        }
        
        func main() {
            fmt.Println("A failure.")
            failure := failureExample();
            fmt.Println(failure);
        }
    

This code is all tested/runs and I might be putting this along with other
issues I've had in a blog post, but no promises. I think this makes it very
apparent why some people have an issue with Go, namely that it feels like
going backwards and ignores lots of prior research at the altar of some
deluded notion of pragmatism. I say that despite a Go Gopher staring at me on
my desk.

~~~
Zikes
I'm not familiar with Haskell so I can't exactly follow what you're trying to
do, but I can take a stab at improving what your Go code is doing:

    
    
        package main
    
        import (
            "fmt"
            "net/http"
        )
    
        func main() {
            for _, url := range []string{
                "http://httpbin.org/status/200",
                "http://httpbin.org/status/200",
                "http://httpbin.org/status/200",
                "http://httpbin.org/status/404",
            } {
                fmt.Printf("%s: %d\n", url, checkUrl(url))
            }
        }
    
        func checkUrl(url string) int {
            response, err := http.Get(url)
            if err != nil {
                panic(err)
            }
            defer response.Body.Close()
            return response.StatusCode
        }
    

You say of your Haskell code that errors are values, and Go is no different in
this regard. checkUrl could easily be modified to return (int, error) and
return both values, rather than panicking when an error occurs.

~~~
codygman
Cool, that's a good refactoring.

What if we don't care about the errors and just want to return nil or the
value. The caveat is that when any of your http requests get a non 200
response code no further processing is done and nil is returned.

~~~
Zikes
What I would do in that case is modify checkUrl to return a bool, change the
return to return response.StatusCode == 200, and change the for loop contents
to

    
    
        if !checkUrl(url){
            fmt.Printf("Non-200 response for %s", url)
            break
        }

------
smegel
> In this environment, a language like Go has the opportunity to shine.

Go has another advantage over Scala (and any other JVM langauge), which also
happens to be it's killer feature - green threads, the ability to write non-
blocking concurrent code in a synchronous style without callbacks. Yes, I am
sure Scala has some "leaky abstraction" over what are essentially just
callbacks, as does Java, but it's not quite the same.

~~~
TheHydroImpulse
No, it's certainly not an advantage in every case, because that's __all __you
can use. It 's not always the best tool for the job. Under the JVM, you have a
__massive __amount of library support for multithreading and concurrency. You
have things available like Futures, STM (Software transaction model), Actor
Model (Akka), parallel collections, etc... Not to mention taking advantage of
immutability, which can simplify concurrency a lot in some cases.

It's not a one-size-fits-all type of game here. So having one choice will
essentially lock you into what you can do with it.

I also wouldn't say a Future, which is a Monad, a leaky abstraction. You don't
really need to understand monads to use them, in fact, a lot of people use
what are monads all the time, without knowing it.

~~~
codygman
> STM (Software transaction model)

I thought this was software transactional memory? Java has an implementation?

~~~
TheHydroImpulse
Oops, not sure why I put that, it's indeed software transactional memory. Akka
is a pretty known library that supports it.

------
threeseed
I wonder if this guy has actually worked in the "enterprise" before because it
sure isn't what he is talking about. Companies like Facebook, Soundcloud etc
are nothing more than bigger startups. Go work for a bank sometime and then
tell me they are comparable to Spotify.

As for Go. What it really needs is a great story around Java integration. JDBC
drivers and libraries need to just work out of the box. Then you can talk
about building cute little micro services.

And Scala's toolchain is pretty simple at least with something like Play.

~~~
frowaway001
Agree. I also wonder what this inferiority complex "hurr durr, $X is not
written in Go, therefore we need to duplicate it in Go!!!" is about ...

~~~
Zikes
Look, it's pretty obvious from your comments that you're not a fan of Go, but
if you can come to terms with the fact that some people enjoy languages you do
not (or their use is mandated by their work environment) then you'll also be
able to understand that they would also like certain tools and libraries
available to them in that language.

It's not about recreating everything in Go "just because", it's about being a
Go developer and wanting those tools to be available to you.

~~~
Retra
This highlights a bigger problem: why should you have to reimplement the same
idea hundreds of times if what you already want to do already exists?

~~~
Zikes
I don't look at it as reinventing the wheel, any more than one would consider
the advent of new languages and technologies a reinvention. They're iterations
on the concept, bringing that concept into a new environment to be further
refined or built upon.

Other threads in this discussion bring up Finagle's shortcomings, partly due
to certain features not being addressed or included by Finagle's authors due
to them being low priority. In that case what you want may not already exist,
but may work as inspiration for another incarnation.

