Featuring
Sponsors
Teleport ā Quickly access any resource anywhere using a Unified Access Plane that consolidates access controls and auditing across all environments - infrastructure, applications, and data. Try Teleport today in the cloud, self-hosted, or open source at goteleport.com
LaunchDarkly ā Test in production! Deploy code at any time, even if a feature isnāt ready to be released to your users. Wrap code in feature flags to get the safety to test new features and infrastructure in prod without impacting the wrong end users.
Equinix Metal - Proximity ā Take your infrastructure further, faster. On March 3rd, join Equinix Metal for their first technical user conference called Proximity. Itās a āfollow-the-sunā day of live-streamed technical demonstrations showcasing Equinix Metalās partners and ecosystem. Visit metal.equinix.com/proximity
Notes & Links
The panel dig deep on code generation in Go. Touching on the new go:embed feature in Go 1.16.
They also discuss IDLs (interface description language) and DSLs (domain specific languages) and the part they play in code generation.
Brian talks about how weāre all guilty of overusing language features, like channels (see Go channels are bad and you should feel bad for an example).
The panel refers to https://litestream.io/ at one point, as an example of a closed-open-source project, and you can read more about on the Litestream GitHub page.
Transcript
Play the audio to listen along while you enjoy the transcript. š§
Hello, and welcome to Go Time. Iām Mat Ryer. Today weāre talking about code generation. Hit it, Brian!
[singing] āTechnical just makes me sick! So code generation always clicked. Write the code that writes the codeā¦ As long as youāre not using Node. Talking ābout code generationā¦ā This is the part where I smash the guitar.
Brilliant. Smash it!
Iāve done that. I did that at GopherCon.
[laughs] Oh, yeah. Well, that was awesome. Thank you so much to Brian there. Now, let me do proper introductions. So - you heard him alreadyā¦ A very early Go adopter, co-author of Go in Action, and co-creator of GopherCon, and actually, one of the original cast of Go Time. Itās only Brian Ketelsen. Hello, Brian.
Itās only me. Iām OG Go Time.
Yes. Welcome back.
Thanks. Itās a little bit surreal being here.
Yeah, it has been so far.
All this guitar playing and you donāt mention that heās in the Gopher Band?
Oh, of course.
Itās true. A founding member of the GopherCon Band.
Hm. How did that come about?
Well, we were thinking about ways to entertain people at the first day after-party, and I thought it might be a fun idea to get a karaoke backup band, so that we could just have like one or two gophers at a time singing or playing an instrument while the band backed usā¦ And it very rapidly devolved into us being a whole band, and we hired some professional musicians to step in if we just didnāt have enough people for a particular song.
It was great fun. And of course, in San Diego the Gopher Band played on aā¦
On a carrier.
Yeah, aircraft carrier.
Yeah, that was pretty awesome.
Thatās quite a gigā¦
Yeah, although I would have to say that probably the tamest year for crowd participation. People were busy looking at airplanes and not so busy thrashing with us. Itās a little sad.
I feel like it would have been hard to claim that as a business trip, when they look at it and theyāre like āYou were clearly at a rock concert, on stage. Thatās not business.ā
Yeah, on an aircraft carrier. You canāt offset that with your tax. Well, I should also introduce ā weāre not here alone; youāve heard his voice already - Jon Calhoun is also here. Hello, Jon.
Hey, Mat.
Welcome back, mate. How are you?
I am doing well.
Good. Good quality chat as usual, Jon. Weāre also joined by Kris Brandow. Hello, Kris.
Hi, Mat. How are you?
Iām very good, thank you. Welcome back. Whatās been going on?
You know, just life; enjoying February, in the Northā¦
[unintelligible 00:05:31.08] Iāve heard that phrase beforeā¦ But well, thatās great. Iām really excited about this episode, because weāre talking about a subject that Iām really interested inā¦ And that is, as weāve heard from our excellent opening song, code generation. So for anyone that doesnāt know what code generation is, who wants to take a stab at explaining what we mean by code generation. Arenāt we all generating code all the time?
Code generation is writing code that writes the code.
Oohā¦ Isnāt that a bit dangerous, Brian? Sort of getting into terminator territory?
Well, itās a little bit like Barry Manilow writing the songs that make the whole world singā¦ I write the songsā¦ I write the songs.
Yeah. So a program that writes programs. So why wouldnāt we just write the programs? Whatās the advantage of doing it in that strange way?
I like to imagine this all started when somebody had a manager who gave them raises based on how many lines of code they wrote, so then like, āIāll write some code that would write some code, and then Iāll get a big raiseā¦ā
Oh, clever. I did actually once get lines at school, and they let me do them on the computerā¦ So I just copy and pasted and printed out the lines, and they accepted it. Because I donāt think they knew how copy and paste worked.
Nice.
Yeah. So maybe it was something like that.
Now, when you say you got lines, does that mean you were forced to write out a sentence manuallyā¦
Yeahā¦
[unintelligible 00:07:02.28] Bart Simpson on the board?
Yeah, sorry, thatās what weād call it, āweād get lines.ā You have to just repeat something, some lesson.
Where I grew up, getting lines meant mirrors and razor blades, and I donāt wanna confuse peopleā¦
Okay, yeah. Well, Iām not as rockānāroll as you, Brian, obviouslyā¦ [laughs] Okay, so - programs then that write programsā¦ Whatās some good use cases for that? What sort of problem does that solve for us?
My favorite is when you have a pattern that you want to apply to a problem set, and you need to do that over and over. You need to treat a particular resource a certain way. And itās gonna be the same for all the resources. There isnāt exactly a generic way to do it, but itās such a cookie-cutter approach that you can write some metadata and then use that metadata to introspect the problem domain and then generate code.
Yeah. Iāve used it before I had a data structure, and obviously, Go doesnāt yet have generics, butā¦ I had a data structure ā I wanted to support multiple types, and I wrote a little program where I could just give it the array of types that I wanted to support, and it would generate the code for each type. So I got strong types, but I didnāt have to write out every version of it.
I mean, go generate is the only generics we need in Go.
Hm.
Oh, boy. Weāre already starting that.
You said āCome armed with an unpopular opinionā, thereās mine - we donāt need generics in Go. Generics make Go harder to read, and theyāre going to decrease my quality of life as a Go programmer. Thereās my unpopular opinion. Tweet that up; put it on Twitter, thatās my unpopular ā generics will decrease my quality of life.
This is sort of rockstar ā Iām struggling to deal with the rockstar. Weāve got a dedicated segment for unpopular opinions. [laughter]
I do things the way I wanna do things. Iām OG Go Time.
Well, weāre here to learnā¦ [laughs] Okay, interesting. So do you prefer code gen then to generics?
Very much so, yeah. I love code generation. I think itās a great way to solve many problemsā¦ Not every problem can be solved with code generation, but a lot of times you find yourself in a position where you have to do a similar set of things for a similar set of resources, and code generation is a great way to make sure that that problem is solved consistently.
Without getting into the generics stuff - for me, code generation probably first popped up when I was looking at things like APIs where people connect with different programming languagesā¦ And Swagger is probably one that everybodyās heard of at this point, and probably the ideal use case for code generation is you have this API that you communicate with JSON and HTTP, and nobody wants to go write a libraryā¦ Like, if youāre a company and your whole company is written in Ruby or something like that, you donāt wanna have to go write libraries so other people can connect to your API in every language under the sun, that you probably donāt know much about. So code generation is like an awesome tool for that type of use case, too.
Hm.
Protobuf uses code genā¦ I think in general serialization - if you want it to go fast, thatās a really good use case for code genā¦ Because you can do it with reflection, and I donāt think generics was really gonna replace any of thatā¦ But if itās a core part of what youāre doing is serialization and deserialization, then having code gen can make it far easier and far more maintainable to write and maintain that code.
So you can have a struct with proper typed fields in there, and they can be set directly, because you sort of know in the meta sphere what those fields are. You donāt have to do reflection and other types of magic to wire that up, that kind of thingā¦?
Yeah, yeah. So in a marshalled JSON or unmarshalled JSON, instead of throwing it into the encoding JSON library, itās like āHey, go use a bunch of reflection and figure what all the field are, and then translate the namesā, you can just generate the direct code that will generate the JSON for you, and that is much, much faster than a reflectionā¦ But also a specialized use case, most of the time; the cost of reflection there is not the bottleneck of your application.
Yeah. I find one problem that we have with code generation is that the code that gets generated tends to be quite ugly and difficult to read. Itās almost like you either have nice templates that you can read and maintain and manage, or you have nice output. I feel like thereās a trade-off between the two. Has anyone found that?
I donāt think there has to be thoughā¦
It used to be worseā¦ Have you used it recently, Kris? Because there was one point in time where it was really hard to remove white space, and I think all the templating libraries in Go have added directives to make that possible nowā¦ But I know there was a time where Mat was completely right, that doing that sort of thing was really hard to do, because white space essentially just caused issues.
Yeah. Step one was generate code, and step two was run go format.
Yeah. If youāre generating Go code, absolutely.
If youāre generating Go code. But there is a package out there by a gentleman named Dave, on github.com/dave. The package is called dst (decorated syntax tree), and it is truly the most elegant solution Iāve ever found for generating beautiful code.
Brilliantā¦ Yeah, because this is a gap, I think, in the standard library. There are packages in the standard library, and theyāve added new ones too, to make it easierā¦ But theyāre so difficult to use, because I guess theyāre the tools that the Go build tools use, and so theyāre kind of all-powerfulā¦ But they seem really low-level and quite difficult to get your head around. You have to do value, value, and itās kind of like reflection, isnāt it?
Yeahā¦ Donāt love that so much. Reflection isnāt the easiest thing to do as a Go programmer. Itās fraught with peril.
Yes, and we avoid it where we can.
I think the big thing that the dst package also brings is proper attachment of comments to items in the syntax tree. I think thatās the biggest failing of the AST package within the standard library, is that you just lose all of your commentsā¦ They kind of just dangle all over the place.
Yes, that is a problem. Does the dst package actually maintain comments, Brian, do you know?
Yeah, it does. That was the biggest selling point that Kris mentioning, is that it will keep your comments and put them back in the right place, even after itās parsed the syntax tree.
Yeah, so thatās great. So thatās like weāre using Go code as the source data, which you donāt always have to do in code generation, I supposeā¦ And also, the Go templates - you can use those. So just for anyone that hasnāt done this yet, it really ā code generation is anything that outputs some codeā¦ But more specifically, if you use Go templates, the text one usually, a common mistake is to use the HTML template, and then you end up with kind of HTML formatting happening in your code, which you donāt want, probablyā¦
So youāll take some source data from somewhere, which could be Go code, and then you mix that with a template, and then you can generate Go code. Thatās one way to do it. Are there others?
But it doesnāt have to be Go code that you generate. You can generate all kinds of things. Generate SQL statements based on a Go structure.
Right.
You could generate anything. And in fact, I gave a talk at Abstractions, the first Abstractions conference in Pittsburgh in 2016 or 2017, called Generate All The Things. Do you remember the meme with the really awkward-looking tech guy with [unintelligible 00:15:09.03] That was me. Generating all the things. Thatās the harder part, too.
Hm. Is that still available?
You know, it probably is. Iāll dig up a YouTube link for it, see if thereās a link for it.
Yeah, weāll put that in our show notes.
But that talk was specifically about my intense love, my burning passion for having a DSL as a source of truth for your systems, and generating the code from that source of truth. So if you want to know information about a type, you look at the DSL. If you wanna handle that type with Ruby code, you generate Ruby code to use it, or you generate C#, or Go, or database code, or whateverā¦ But that DSL is a system of record for all of the information in your enterprise.
I have a question for you - weāve talked about this several times on the show, where in languages like Ruby or any dynamic language you can dynamically catch if a method is missing, and then you can add code. Do you consider that code generation as well, or does it have to be something where ā in Go you actually have to spit out some Go code that can be compiled, because thatās how Go works. So do you differentiate between the two, or do you kind of consider the two similar?
Well, I would say code generation youāre writing new code, and in Ruby, using something like method missing - thatās more meta programmingā¦ Although theyāre really close cousins, I donāt think theyāre exactly the sameā¦ But theyāre similar. If we have time at the end of the show, remind me to tell you about how method missing cost the state of Georgia seven million dollars.
Yesā¦ Thatās definitely going in. [laughter]
I do have to say, when it comes to code generating, specifically when youāre trying to generate Go code, I feel like the text template package is actually kind of bad for that, and itās better to write actual Go code and then use the tools we were talking about to parse it, and then kind of manipulate it that way and stamp it outā¦ Because then, when youāre actually writing the template, youāre writing real Go code, so you get syntax highlighting, you get formatting thatās really nice, you get compile errors, and all of that nice stuffā¦ Whereas when youāre just working with text templates, itās a little bit wonky.
So how do you do loops and things in that then, if youāre gonna iterate through a set of data? How do you do that in the Go code?
Um, for range?
Okayā¦
I mean, since itās just like the templates, too - youāre just taking the templatized code. You usually have to wrap it in a function or something, but youāre just like pulling out the guts of a function, and then your IDL or whatever will specify and youāll have the glue code that will be like āOkay, now you need to stamp this thing out within this function multiple times, or within this domain multiple times.ā It does look a little bit different than what templates look like, since you donāt have that kind of in-built looping and variable assignmentā¦ But I think that can also help make it a little bit cleaner, because then youāre kind of separating things out a bit more, and your templates can just be like āThis is the prototype of what I want the code to look like.ā
Yeah, thatās great.
That same person Dave, who wrote dst, also built an application called Jenniferā¦ And Jennifer builds on top of dst to help you generate code - hence Jennifer. Because whatās a programming application without a great punny name? And Jennifer is my personal favorite tool for generating code, because it builds on top of that dst, and it lets you write functions based on the occurrence and order of syntax in your AST. So youāre parsing your syntax tree and you find this particular Node, you can write a function that generates code based on the state of your current treeā¦ And itās really, really nice.
That is cool.
Itās the cleanest generator Iāve ever seen.
So how does that look when youāre writing the code? Do you literally have types like functions, and thereās some kind of DSL/build-a-pattern type API or something like that?
Yeah, itās really just types and functions. So youāve got your AST, and you do some sort of walk. Thereās always a walking function, where you walk the AST, and you build up stateā¦ And as soon as you hit some sort of desired state - youāve got the right number of nodes in your tree - then you trigger a function that generates code, because you have enough state. But those functions are things like ast.createFunction.
Right.
And each of those also takes a function as one of the parameters. So you have these nested functions. Itās a very powerful paradigm, but it can be hard to follow. The first couple times you do it, you kind of tilt your head sideways and āWhat the hell?ā
Yeah. So it takes a bit of getting used to. But any of this meta programming is a bit like that. Itās a bit like Inception, where youāve gone into someone elseās dream, isnāt it?
A bit, yeah.
A little bit.
A little bit.
Yeah, a little bit.
I feel like the reflection package is a lot like that, tooā¦ The first few times you use it, especially when youāre trying to do something clever, like calling a function, and youāre just kind of like āWhatās going on? Iām very confused about what Iām supposed to be doing here.ā
Youāre trying to call a function on a pointer type of a struct, or an interfaceā¦ [laughs] Have fun.
Yeahā¦ And itās like, āOh, I have these values, but I have to turn them into reflect valuesā¦ How do I do that?ā Itās confusing at first, but I also feel like once you understand the reflect package, thereās a certain class of code that you can just write, and it becomes so much easier. Iāve found thereās tests that Iāve written where I would have had to have hundreds of lines of more code to express them, but since I know how to use the reflection package, I just sat down and wrote them out, and it was like 30 lines of codeā¦ And now I can run basically any type of function that I want, and assert values, and all of this stuff.
Yeah, the problem is you donāt know itās wrong until you run itā¦ Because of course, itās abstract. So itās not like you get the compiler to help you check the things youāre doing make sense. Thereās a lot more runtime in reflection. But I like writing tests, so itās not so much of a problem.
You like writing tests?
I love it. Actually, sometimes thatās all I do.
It sounds pretty boring if theyāre all failing.
[laughs] Good point.
I would definitely say that my experience with generation is kind of similar to my experience with reflection as well, in the sense that ā Mat, you were saying you donāt know that a reflection is wrong until you run itā¦ But I honestly felt like with generation I was almost in a similar boat, where I had to actually run it knowing what I wanted is the output, to see if it did itā¦ And sometimes it was just because I didnāt understand something was happeningā¦
Yeah.
But once you get it, itās easier. It just took a little bit to actually make sure you grasped everything correctly.
Years ago I wrote a package with a friend of mine called Jennyā¦ And theyāre not to be confused with Mark Batesā package Jenny from Go Buffalo, where he just - as far as I can tell - stole the name from meā¦ But luckily for him, Iām not very litigious. But weāll see. If he keeps texting me the things he texts me, maybe that will change. Anyways, this package did what you were describing, Kris, where there was a special type. So it was real Go code, and it was just like an interface type, but it had a special nameā¦ And then the tool would process that, and then you were able to replace that type with other types. So itās kind of like generics, very lightweight generics. Itās still used by people, but I donāt knowā¦ But thatās kind of a nice way to do that, because then, like you say, the real code, the source code, the template is real code.
Is there anything you can do to help yourself in that way? Go fmt is a great one is a great one if youāre generating Go codeā¦ Although I suppose if youāre using that dave/jennifer package, it probably formats it for you, doesnāt it?
It does, yeah. It outputs code in the right format.
Yeah, thatās brill. Some people put code generation as part of their continuous integration build. I prefer it to be a developer time exercise. How does everybody feel about that?
Ooh, the can of worms is openā¦ [laughter]
Are there any good cases for putting it in the CI? Because I feel like you wanna run the tools to generate, and then run your tests, and then you push it, donāt you? Whatās the use case for having the continuous integration generating code?
Salt and pepper push it? [laughter] Yesā¦ Come on now. You guys arenāt that young. So I would say that the biggest case against developer-side code generation, checking in generated code, is the fact that you donāt have any control over the tool you use to generate the code. If youāre using version 1.2 of the code generation, but youāre expecting 1.4, then nobody will know that until some point later onā¦ And using your CI environment to create a known good code generation would be the antidote to that problem.
Yeah, interesting.
I donāt knowā¦ You could easily play either side of that fence.
Yeah. Actually, Iāve been playing around with Codespaces on GitHub, and that essentially gives you a VS Code virtual environmentā¦ And you can, just by specifying a Docker file, you can actually install all dependencies, and things like that. So thatās a really interesting idea, to have these controlled dev environments. It would address that particular problem.
But what about go generate? Because go generate is a command that you have to run explicitly; go build doesnāt run any go generates. Maybe we should just take a minute to explain what go generate is, for anyone unfamiliar.
We should take a minute for that. So go generate is another magic comment, yet another magic comment (can we put that in magic emphasis?), yet another magic comment in Go code that does somethingā¦
Yeah, weāll flash it on the screen.
ā¦non-programmatic, that specifies a command to be run when a file is triggered by go generate. So you type go generate from the command line, and any Go code that has the go generate comment will run the command after the go generate comment. And that comment doesnāt have to be related to Go. It quite literally can be any shell command for your computer. So you could say āgo generate spotify open playlistā and everytime you run code, it will open Spotify and play Guns Nā Roses. So go generate is literally just a trigger to cause a thing to happen.
Yesā¦ By the way, everyoneās gonna be doing that now, arenāt they? [laughs]
Why wouldnāt you.
Yeah. Theyāll probably play that song. Or play your song, probably, from the opening of the showā¦
[singing] Talking ābout code generationā¦
Iād love that to pop out of my speakers everytime Iām doing thatā¦
Everytime youāre buildingā¦
Yeah. Could you do like a whole album, and we could have one for failed tests, andā¦?
Sure, sure.
You could do the whole set.
Youāve got Sweet Child Oā Mine covered, so Iām not sure if thereās enough room for the two of us in this industry. I bow to your superior capabilities.
Oh, no. No, no, no. Okay, so are there any common gotchas or something that we should look out for when it comes to generating code?
Absolutely. Tests - 100% required. If youāre gonna generate code, then you need to have known inputs, and expected outputs, and test against those as a unit testā¦ And then as integration type test, a broader test, test that the code that you generate does what you expect it to do as well.
And would you say not to generate the test code?
I actually love generating test codeā¦ So the question is āThe test code that you generate actually tests your code?ā Who watches the custodians? I donāt remember the Latin key custodianā¦
Who watches the watchers.
Whoās watching the watchers.
Yeah.
So to make sure Iām getting this right, youāre saying that youāve enjoyed generating both ā the code is gonna do whatever you need it to do, plus generating tests for that code. I just wanna make sure weāre all on the same page.
Thatās correct.
Okay. Thatās something Iāve always sort of ā itās not that Iāve been against it, itās just that Iāve always, I guess, veered away from itā¦
Why?
It almost felt like I wasnāt actually ā how do I put this?
Donāt worry about offending Brian.
Yeah.
Iām not trying to offendā
Yeah. I could care less.
It felt almost like those tests never actually failed or told me anything useful at the time I was doing it, so I kind of just stopped doing it at the time.
Thatās the bonus of code generation, because you generate code that works, because youāve extracted it out of a pattern that already works. Itās not like you start generating bad code, and then you catch it by writing a good test. I always start ā actually, I shouldnāt say you, because I donāt know what you doā¦ But I always start with a pattern that I know that works, and then I abstract that into code generation. And once you have that, then you can write your tests against it, and you can generate them.
Yeah, I feel like those meta tests, the ones that youāre talking about, either youāre testing the source thing, the original thing, or those integration tests; I feel like theyāre extremely useful.
Itās very satisfying when you start doing code generation and you see it working with just one bit of data, one input, and then you just add all the data and you suddenly just see all this code being generatedā¦ It really is very satisfying, whether it should be or notā¦ Donāt you find?
Oh, very much so. A moment of eye-openingnessā¦ What do you call that? My a-ha moment for code generation was when I stumbled across Goa in 2016 or 2015 (I donāt remember). And Goa is interesting because most DSLās that you find in the programming world arenāt written in Go. Go is not an easy language to use to write DSL. You can use Go structs as the source of your code generation, but actual Go code functions - not pretty. Ruby? Python? Beautiful languages for writing DSLs. But Rafael, who wrote Goa, found a way to build DSLs that was elegant.
And when I saw that, I immediately demanded in my open source friendly sort of way that he take the template engine, the DSL engine, and make it generic, so that it wasnāt just Goa that you could write DSLs forā¦ And he said āPull request speak, baby.ā So that was the first thing I did - I took the DSL engine and made it generic, so that you could create any DSL with the DSL engine for Goaā¦ And that was it. I was so sold at that point. Because youāre writing what looks like a DSL, youāre describing your system, and that builds a syntax tree that then executes code that writes code for you. And that code can be anything. You can go from the gamut of writing SQL IDL files, all the way across the spectrum to writing Kubernetes deployment manifests, with the same DSL. Thatās brilliant.
Yeah.
Why wouldnāt you do that if you could?
I think thatās probably the big appeal for any code generation. Mat, youāve mentioned Oto before, and you use that to generate both Go side code and JavaScript code to connect to itā¦ And just having that idea of the developer only has to do something once is very appealing. I suspect thatās also why, when we look at code generation, there are several tools out there for generating basically models to connect to a databaseā¦ And I think thatās appealing to a lot of people, because they think āI donāt wanna write all this code that connects to the database.ā
And I know people have mixed feelings about using something like that versus not, but it is by far one of the more common use cases I see, and I think itās because people are just attracted to that idea of āI only have to define this stuff once, and then all the other code for it is generated.ā
Jon opens that ORM can of worms early in the show today. [laughter]
I mean, it had to come up at some pointā¦ And the ORMs generally take two approaches when theyāre generating code, of either āGo is the source of truthā or āSQL is the source of truth.ā And I think thatās probably one of the bigger can of worms to get into, as to which oneās right and which oneās gonna cause you more problems down the road.
Mm-hm.
Whatās the answer then?
Personally, I guess it just depends. Iāve found more luck with things ā I think sqlboiler is one that takes the SQL and generates the Go codeā¦ And Iāve found more luck with that type of approach most of the timeā¦ But there are definitely cases where the opposite is easier. For instance, if youāre taking somebody whoās new to developing web applications and theyāve gotten some Go down, at that point itās probably easier to use one that takes the Go and generates whatever it needs for the database side, because they already get what the Go isā¦ But it really just depends on the team and what youāre doing in that sense.
Iāve definitely seen cases where if you have the Go stuff defining what the database is, and then some migrations or changes can be kind of tricky to express correctly for some people, or itās easy to have some weird bugs like that, where the Go code changes and the SQL doesnāt necessarily change the way you need it to to reflect thatā¦ But I donāt know, I donāt wanna get into the ORM can of worms. I have mixed feelings about them. Theyāre definitely a tool that Iāve found use for, but most people seem to hate them in the Go community, for whatever reason.
You know, thereās no shortage of opinions in the Go community, which is really strangeā¦ Most communities donāt have any opinions, but the Go community is full of them. [laughter]
Yesā¦ And weāre also cordial to each other, too.
Thereās another interesting use case Iāve seen for thisā¦ Ernesto JimĆ©nez did it in Testify. Thereās two packages in Testify for assertions. Thereās the Assert package, and then thereās the Require package. And the only difference is one will fail immediately, and the other - you kind of carry on and collect failures up and present them all in one Go. And a lot of people prefer tests when they fail to just fail at the first thing, and then you can just address that, and then work in that way.
That Require package is generated from the Assert package, so it kind of guarantees that the two APIs are the same, or similar enough, or different in the same way; Iāll think of what the correct way to phrase that is later, and weāll edit it in. But I thought thatās quite a clever ā
Orthogonal?
Yeah, thank you. I didnāt do computer science, so I donāt know what Iām talking about. Yes, that one. Itās a nice use case, because in that sense, you want to carry over the knowledge from one of those packages to the other and use them interchangeablyā¦ So I thought that was quite a clever way to do that. What do you think about that?
Yeah, brilliant.
We canāt all be winners.
I mean, it sounds like a good use caseā¦
Brilliant. Thanks, Chris. We appreciate your opinion. Good stuff. [laughter]
One of the interesting things for me about code generation is that I feel like you see a lot of ā like, Matās defining a case where you had one set of code, and basically it sounds like you didnāt wanna have to write it twice, which makes senseā¦ And the simple solution was just āWell, Iāll just generate the second version.ā
Another approach to that would be to take the code and abstract it away, so that you have one thing that can be used in both casesā¦ But at the same time, that requires some work, so itās interesting how people use it to solve different problems. Itās not like itās the only solution, but itās definitely a solution to the problem.
Yeah.
Yeah, I feel like code generation is one of those things like SQL normalization; youāve got N levels of normalization in SQL - third normal, fourth normal, fifth normal formā¦ Code generation is kind of the same way. Level one of code generation is taking this template and plugging in a variable here or there and spitting out some code. Level two is maybe throwing some f loops and some statements in thereā¦ And you get all the way up to level five - youāre introspecting the whole system, determining the needs of the many versus the needs of the few, and generating all the other code that needs to be generatedā¦ Thatās a different level of code generation, a different plane.
Yeah, thatās true. Have you ever seen code that generates code that generates code? That sort ofā¦
Iāve written ā Aaron Schlesinger and I wrote code that generates code that generates codeā¦
Wowā¦
And it was a lot of fun.
Wow. Is that easy? Is it difficult to follow?
It just takes a lot of iteration and patience. I wouldnāt say itās hard, because itās not harder than writing any other code; it just takes a lot of time to build up small layers of success, so that you can have bigger layers of success.
Yeah.
Lots of mental gymnastics in that too, to figure out which layer youāre sitting atā¦ As youāre generating, youāre like āOkay, wait, am I doing this correctly? Am I in the right spot? Am I thinking about this in the right mind frame?ā I think that is one of the big challenges of generating codeā¦ Itās not actually writing the code gen itself, but just wrapping your mind about what youāre trying to do, because it is so much more abstract than just writing out code by handā¦ Which is also what makes the Reflect package also very complicated to use, because youāre not using the same utilities and handles that youāre used to using when youāre writing Go code.
That was the big pull for me from Goa, because I was writing a DSL about my expectations of what I wanted when I was done. I want a web service that listens on port 8080, that exposes this API with this resource, that has these methods, and it returns these fields, and it expects this authentication. Thereās nothing in that about how Iām gonna do that; thereās nothing that says āIām gonna use go-kit, Iām gonna use this middleware here.ā Iām just writing my expectation, and then the code generator does some stuff, and hopefully the code generator meets my expectation. But if it doesnāt, I can change the code thatās generated until it does. That was the power of expressing what I want. Itās like test-driven development when you think about it. Youāve written your desired outcome first, and then you just keep writing code until itās green.
I think that IDLs and DSLs are a kind of unexplored spaceā¦ Not just in Go, but across the industry, where thereās not a lot of emphasis and effort put into designing good, standalone IDLs, but I think thatās also crucial to using code generation wellā¦ Because I know in the past when Iāve tried to write some code generation, Iād usually start with āOh, Iād like to just use Go, and then decorate a struct with a bunch of stuffā, and Iāve always wound up at just āI should just write a custom IDL or DSL in some serialization format, and then just use that as a source of truthā, because now Iām getting all of this stuff mixed in, of like āOkay, I have to parse this package now, and look for this special Go type, and then introspect it, and then generate code based off it.ā Itās like, just read in an IDL file and then use that to generate everything. But I feel like when you get into that realm, youāre getting into the āOkay, now I either have to pick up a serialization format that exists, like YAML or JSON, or I have to go write a parserā, which is getting in even deeper if you wanna have a truly customized IDL that you can use.
The question is how deep do you need to go? I mean, do you have to write a parser? Do you have to write your own IDL? If thereās one small problem and you just need a little bit of metadata, then throwing a struct in Go and then inspecting that - thatās the solution. Even though itās the cheapest and easiest way to do it.
Yeah, absolutely. I think thereās definitely levels to this, where itās like - if youāre making, say, a stringer, which takes a number of enumerated things and then makes the string output for them [unintelligible 00:41:35.26] I think for that itās relatively simple and straightforward, and you donāt need an IDL. But I think when you start to get into the really bigger code generation systems, I think it is kind of good to shift away from trying to shove everything into Go. I think thereās a point where you start really feeling that friction, when youāre like āI donāt think that this is quite rightā, and I think thatās the point at which you should make the job. I donāt think you should really start with designing your own IDL or DSL or serialization format. I think those are things that take a lot of time and expertise to do, and you donāt wanna be learning that while youāre also trying to figure how to do code generation well. I think you have to take your steps.
But I think that itās a very important thing to recognize that at some point if youāre doing a lot of heavy code generation, what you started with - youāre gonna outgrow it, and youāre gonna need to do something different, and just be prepared for that.
Yeah. Itās one of the most fun things and rewarding things Iāve ever done as a programmer. So I would absolutely recommend start as small and simple as you want to, even if itās just a tiny little template that youāre using to generate other codeā¦ And just grow with it. Itās fun.
I feel like both meta-programming and code generation to me always felt like I was cheating, or pushing the limits of what I was supposed to be doingā¦ Itās almost like I had to be secret about what I was doing, because itās like āIf other people see this crap Iām pulling, theyāre gonna be like āWhat is this guy doing?ā
Really, it felt naughty?
It felt kind of naughty in some cases, because youāre doing these things that are well beyond just writing your traditional code, of like āOh, I can see exactly what it does.ā When youāre generating code, itās this weird ā I donāt know, it felt kind of naughty for some reasonā¦ But it was also really cool, seeing how you could push the limits of what you could actually achieve, and trying to ask yourself āWell, can I do this?ā And sometimes, at least for me, I wasnāt that productive some days, because I would go down some rabbit hole just to see if I could do something. Not because I needed to, but because I was like āCan I do this?ā
Yeah, but thatās alright. I think youāre a good boy for doing that.
[unintelligible 00:43:42.11]
I think thatās actually something we need to do more of, really. I think the only way you can really acquire these skills is by going through and actually practicing them, and actually using these libraries and packages. And if every time that we have a small problem weāre like āOkay, I could solve this with a reflection, or I could solve this with code generation, but I could just write these 150 lines of code by hand and it will solve this problem nowā - if you always choose to write the code by hand, then youāll never have the skills to actually use these packages. So when you come into a big problem where you do need these heavier tools, youāre just kind of stuck or youāre lost, or youāre just gonna take up an exorbitant amount of time going through and not using these tools.
I know that multiples times in my career I have been able to lean on my ability to use the reflect package in Go to save myself tremendous amounts of time and tremendous amounts of stressā¦ But thatās only because I sat down and actually pushed myself to use the Reflect package, even when there could have been better solutions, or it wasnāt gonna give us huge gainsā¦ And I think thatās something we as software engineers need to get a bit better at, is not trying to always optimize for right now. I feel like we do a lot of that, and I feel like thatās usually the argument against this type of exploration. Itās like, āWell, you could have saved four hours if you just hadnāt done that thingā, or āWe could have saved some time if you just hadnāt gone through and done that exploration.ā And I think that also kind of pulls us back into this frame of overly-focusing on code.
I think what you said there, Jon, about it feels like youāre cheating - I think it adds a lot of color to how we as engineers approach what we do; we focus a lot on the code, we focus a lot on the actual work output thereā¦ But I think software is about so much more than code - we have to write design docs, and specifications, and all these other thingsā¦ And I think the more that we start to pull ourselves away from the intense focus on code, the easier itāll become not to only do these code generation/reflection-based things, but also to get to the more promised land of writing more comprehensive design docs that can solve bugs and solve problems before weāve even written code and we have some maintenance nightmare to deal with.
I like to look at it this way - I am so lazy that Iāll spend three weeks writing a code generation system so I donāt have to spend two days writing repetitive code.
Yeah. I feel thatās a fair point. Itās funny - this idea of doing experiments, and stuff. If youāre in an environment where youāre constantly late, the projects are constantly lateā¦ Usually, because people in the past - including yourself sometimes - have estimated it and youāre just wrong, because itās really difficult to estimate accuratelyā¦ And so for no good reason really youāre just lateā¦ And then thereās no room for those kinds of experiments, and things. So I think it also falls to managers and tech leads and people to make sure that thereās space in teams for people to do those kinds of experimentsā¦ Because you know, one definition Iāve read of creativity is making connections that werenāt there before. And the more fuel you can give that process, the more opportunity for creativity there is. So itās something I believe in a lot. Unfortunately, itās quite rareā¦
I speak to people ā Iāve been quite lucky, but I speak to people where theyāre just always late, everything needs to be done quickly; thereās no time to do an experiment to see if the Reflect package is gonna be interestingā¦ And also, if it then isnāt the way you end up doing it, some people see that as then a failed experiment, which itās not. In science you only really learn from things when they go wrong, when they fail; you learn a lot from them if they do.
We have a tweet just come in ā not just come in; Iām just trying to make this feel more liveā¦
Way to add the drama, Matā¦ Thank you. BREAKING NEWS!
Breaking news! This just in, from someone whoās using them. Itās very apt, itās gen20. James Nugent says āTo commit or not to commit.ā More of a discussion point, probablyā¦ The first half of that was more Shakespearean than the second halfā¦ But what do we think? Do you commit your generated code or not?
Well, itās this discussion weāve started with, right? Do you do it at the developerās desk, or do you do it CI?
I feel like a lot of this just depends on the workflow youāre working with, because ā I can give you an example. I use Tailwind all the time, and it generates a massive CSS file of all this stuffā¦ And committing that big thing doesnāt really make sense half the time, because anybody whoās running the app locally - like, if I have a React app, the toolchain is set up to automatically generate that anytime a file changes.
Right.
So at that point, committing that just doesnāt make a lot of sense. Now eventually, if youāre deploying, you have to commit whatever the cleaned up version it is that youāre shipping withā¦ But for the development side, I would say thereās no reason in the world to commit that. But on the other hand, if youāre generating ā I donāt know, itās almost like if you have a libraryā¦ Like you were saying, Testify, and they generate code. Well, they pretty clearly have to commit that; somebody has to commit that at some point. Now, where and when it gets committed is kind of up in the air I guess, whether itās your CI tool or developerā¦ But it definitely depends on your flow of how things get generated.
Yeah. If you need it locally, you will always generate it locally probably, because otherwise ā thatās like fire and forget.
If you didnāt need it, why are you generating it at all?
So yeah, you do need it locallyā¦ And yeah ā Tailwind is actually an interesting point there.
I feel like thereās this line between āIs what youāre distributing code, or is what youāre distributing a running application?ā I think if itās a running application, like Jon just kind of mentioned, I think in a lot of ways it makes sense to do the generation at the point where youāre actually gonna be running the thing. But I think if youāre a library maintainer, itās much better to commit the code. And I think thatās one of the things behind go generate and why it runs the way it is, because itās like, the person who gets your code might not have the generation tools or the version of the tools that youāre using, to actually be able to successfully generate the code. So it makes library usage a lot harder if the person using the library has to do this code gen now.
Yeah. And you want people to be able to check out the code and run it and build it, donāt you? You donāt want those extra stepsā¦
Exactly. And if you donāt have developers checking in the code, what you end up with is Bazel.
Right.
You have to boil the entire ocean every time you wanna build a tiny appā¦ And thatās where Bazel is. Itās an ocean boiler.
Yeah. Thatās why I see code generation as a dev time exercise, for that reason, and others.
This just in! ā¦I have another one from a website called Twitterā¦ And this is from Tobias. Tobias asks āHow would you generate code split into multiple files or directory, to get you started for new projects or parts of projects?ā So has anybody seen code generation for bootstrapping projects? A bit like how Rails used to have that rails generate command.
One of my first forays into code generation was a tool called GenKit, that generated a go-kit microservice directory structure for youā¦ And it was like a first-level code generation, with ugly templates and struct tagsā¦ And it worked really well. It could generate all of the boilerplate that you needed for a go-kit microservice. And that included many directories, and many types. It was very inelegant, but it was also very efficient. It took me half a day to write it, and it worked really well.
Yeah. So what more can you ask for.
Yeah. On the opposite of that spectrum is Goa, which - you know, the DSL doesnāt take long to master at all, and it will write code across dozens of directories if thatās what you want. The background behind it - thereās a lot of code behind that.
Yeah.
I feel like this question is sneakily asking us how we would design a frameworkā¦
Buffalo.
ā¦like Buffalo, or somethingā¦ Because it does kind of sound like ā you know, how would you structure it? It basically comes down to āWhat would your framework look like?ā at that point. And if youāre interested in examples of that, Buffalo is a good one to go check out. It generates everything, and then thereās much smaller tools, like gqlgen I think is one that just generates the GraphQL components, and then youāre still expected to connect everything to that. So thereās completely different takes on that that you can look at, and I think it kind of just comes down to what you need for your projectā¦ Because thereās definitely cases where Buffalo is a great fit, and thereās definitely cases where it probably wouldnāt be my first tool to go to.
Well, Buffalo is a good example of solving a problem and then abstracting that solution out into a more generic toolkitā¦ And thatās really where code generation shines for me - once youāve got that problem solved, generate the code to solve it over and over.
Yeah, thatās an interesting point that youāve made a couple of times, Brian. I think it is something that is worth people consideringā¦ Because even if you know youāre gonna use code generation to solve a particular problem, you still ought to solve that problem yourself firstā¦ A bit like when you have a sense that thereās a call abstraction just waiting to be discovered. Itās worth solving the problem ā really solving it for yourself first, and then having a look to seeā¦ And in fact, sometimes doing it a few times, actually. I think thatās quite a nice point.
Kris, have you done actual code generation yourself? What sort of use cases did you use them for, do you remember?
Yeah, yeah. I think the last big thing I did code generation on was when I was working on the MongoDB Go driverā¦ And the kind of wire protocol level was very normal and regular, and I would just keep writing the same code over and over and over againā¦ This is really annoying. Itās like slightly different things, like āOh, this command is slightly different than the other oneā, so Iād just wind up starting off with just writing the structs, and then generating all of the methods that I needed off of themā¦ And then I actually wrote an IDL format, and I was like āOkay, now we just have these IDL files, and we generate all of the implementation based off that.ā
So itās like one of the things Iāve used code generation for in the past. But Iāve used it for a lot of things that are similar to that, where itās like āI could write and maintain all of this code by hand, but itās very tricky, and everything is very regular, and itās gonna be really difficult to find bugs if I try to maintain this by handā, because it will be like small typos here and there will completely breakā¦ But in the case of the Go [unintelligible 00:56:03.24] then it might only break when itās out in production already, because we donāt have a good way of testing that already, because of other things. So I think thatās how Iāve used code generation a lot in the past.
To kind of respond to this tweet too, I have kind of a split way that I feel about this. I think that these types of tools that generate scaffolding and whatnot are good and useful, but I also very much dislike that we use things that are so general and generic that we need all of this boilerplate in the first place. I feel like itās somewhat of a failing of design hereā¦ Because I feel like Iāve run into these same sorts of situations before; when Iām trying to design something, Iām like āOh, thereās a lot of boilerplate.ā And each time that Iāve done that, Iāve just sat down and spent probably a considerable amount of time figuring out why I am writing so much boilerplateā¦ And usually at the end I figured out, āOh, my design didnāt quite fit properlyā, and I feel thatās what a lot of frameworks wind up doing - they try to be so generic and encompass so many things that you need all of this boilerplate to just get the thing that you could have written with a few lines of code if you just wrote the more specific code. I think a configuration library is a really good example of this. Iāve written multiple configuration/parsing libraries that wind up being less code than if Iād used Viperā¦
Oh, donāt get me started on Viper, Krisā¦
[laughs]
Viper used to be the cutest little tiny configuration thingā¦ And now itās the biggest monster. And Iām responsible for a good amount of that sprawl, because I added the remote configuration so you could pull configuration from Consul and Etcdā¦ I added that to Viper. And it turned Viperās binary from this big into ginormous, and itās a big, ugly mess of code. I did itā¦ I did that!
[laughs] Well, welcome to therapy. Thank you for attending.
āWho taught you how to smoke weed like that?!ā āYou did, dad!ā
Okayā¦ [laughter]
But I do think that for where we are right now, having these code generation tools does make it easierā¦ People that are like āOkay, but I have to use these framework because of other reasons.ā Letās at least make their life a bit easier. But I do worry about the kind of āLetās just generate all of our structuresā, because then itās like I go and open an applicationā¦ And thereās a little bit of a nice thing about when you open a well-structured Go project where youāre like āI can see where all the things go, I understand, I can see what this application is doingā, but if you start having all of this hierarchy and structure, it becomes like āOh, I can tell this is a Rails project. I have no idea what it does, but I can tell itās a Rails projectā, or Buffalo, or whatever project. āI can tell that itās this thing, but now Iāve gotta go and do a whole bunch of extra investigation to figure out what it actually does.ā
Yeah, thatās a good point. Thereās no opportunity for storytelling, is there? If all the structures look the same. Because thatās the thing - when you have good package structure, it tells you a story at a glance. Thatās a great point, Chris.
Amen.
Yes.
I donāt wanna throw a particular static website generator under the boss, so Iām not gonna mention Hugo by nameā¦
Okay, very good of youā¦ [laughs]
ā¦however, I had this idea. I wanted to play with Go 1.16 yesterday, and I have a Hugo blog, because Hugo really truly is a fantastic way to build blogs. Itās a wonderful way to turn markdown into HTML. Itās opinionated, and itās fast, and itās wonderful. But I thought to myself āIām gonna use the Embed package in Go 1.16, and Iām gonna use something like Gin, or Echo, or some quick little router framework for web apps, and Iāll just go steal the code from Hugo, or go import the code from Hugo, that parses my markdown and uses the same templates that I already have. Iāll embed that into the Go binary and Iāll ship a web server version of Hugo; thatās all embedded in the same thing.
I dug into the packages of Hugoās code, and Iāve spent almost an hour and a half just finding the one thing that triggers the functions to execute templates.
Right.
Thousands of packages. Thousands. Maybe millions. Children diedā¦ [laughter]
Yes, I do feel thatās a thing that happens though when you start to get bigger and bigger libraries - all of a sudden, finding really simple and straightforward things becomes an archaeology experiment. I remember digging around - this is probably the example of examples, but Kubernetesā¦ I was like, āHow does this thing work?ā And I was like, āOkay, I guess Iām just never gonna knowā, because the sprawl of packages that you can wind up with is just insane.
Yeahā¦
So I feel like code generation - good, but also maybe we should make sure that when we are code generating, that weāre also being good citizens and being good helpers and maintainers, and making sure that new people can get in and start to understand how things are structured.
Yeah, thatās definitely true. Another thing that happens in open source as well is when projects get popular - suddenly, everyone wants to contribute to themā¦ Which is great, but thatās then what you see happening. It did happen to Hugo, I must agree, Brianā¦ Because it started off ā it was kind of tiny and it couldnāt do that much, and that was sort of really easy to use. And it is just a trade-off. The more features a thing has, the harder it is to use, by almost its natureā¦ Unless theyāre just more capabilities within a certain direction, like just adding more storage providers that it can support. That idea is still the same, of storage providers, so thatās quite a nice way to scale out a project.
Itās when things start to do more and more thingsā¦ And Ben Johnson - he recently has his project, and he said āNo PRs. No contributions, please. This is an open source project, you can use itā, I think he lets you open issues to report bugs, but it doesnāt want code contributions because itās a hard problem to solve. It really is.
Yeah, I really respect that. It probably took a lot more courage that weāre willing to admit to come out and say āI donāt want your PRs.ā But Ben is a really smart guy, and he went through this with BoltDB, and I think that learning experience taught him quite a bit about how to manage that open source. So youāre welcome to the source code, itās open, but youāre gonna have to fork it if you wanna change it.
I feel like that should perhaps start being the way that we interact with open source more. As we just mentioned, with Hugo - how many of those things do we actually need? How many of those things do most people actually use? Wouldnāt it have been better if we kept Hugo simpler and just made it more extendable? Or maybe easier for people to fork and add the things that they needed to add.
So my experience as an open source library maintainer is that thereās just a lot of drive-by PRs, and you put a lot of effort in and you put a lot of energy into explaining to people āHey, this is why we really wish we could add this feature, but we canāt.ā Or āHey, we can add this feature, but we have to do it in a different way.ā
I read Benās article about why Litestream is open source, but closed to contributions, and it really resonated with meā¦ Because itās like, it does take a huge amount of emotional energy and time to take contributionsā¦ And I think a lot of the time it just doesnāt wind up being that kind of ā it doesnāt wind up paying off for the maintainer, and sometimes for the community as a whole.
Yeah, yeah. I like that phrase, the drive-by PR. I think thatās really cool. I donāt get drive-byās. Youāre not even meant to be textingā¦
We mentioned earlier the go embed new feature in Go 1.16. Next week Mark Bates himself will be joining us to discuss that very topic.
THE Mark Bates?
Yeah, itās the only one we could get, unfortunately. I did try and find alternative ones that were nicer, but unfortunately I couldnāt find any, so it has to be that oneā¦ Yeah, sorry.
I think that go embed is actually something that Iāve been wanting to have for a while when it comes to code generationā¦ Iāve had tools where I distribute them to people internally, within our organization, and one of the challenges is always like āHow do I get the templates (where the templates are go-kit or a text template) into the binary that Iām shipping to people?ā And I like that now itās part of the language. Itās a language feature that enables you to do this. Because Iāve definitely spent a lot of time with people just explaining āOkay, this is how you compile this tool so that you can use itā, and I think that having go embed there will just make it so much easier; you donāt have to do anything special to make it work.
Yes. And if you wanna learn more about go embed, tune in next week. Thatās my professional radio bit.
Nice.
So weāve gotta let Brian finish his therapy session by going into his unpopular opinion.
Yeah, I think itās timeā¦ Itās time for Unpopular Opinions!
[01:05:57.12] to [01:06:16.09]
Okay, well - we did hear your unpopular opinion earlier, Brianā¦ Do you want to elaborate on it, or do you have any others?
That was well over an hour agoā¦ I donāt even remember what it was.
You donāt want Go generics.
Oh yes, genericsā¦ I really donātā want Go generics. I feel like Go is a wonderful language without them, and one of the core features of Go is that itās easily readable. Go is optimized for readability. Adding generics reduces that readability significantly for me.
Reallyā¦ Somebody on Twitter replied to me ā I wonāt say that personās name, but they said āWTF? Has no Go programmer ever worked with generics before? You are behaving like kids.ā
Somebody said that?
Somebody said that. Iāll send you that, because you might get on with that person, Brian.ā
Maybe. I mean, I like generics, donāt get me wrong; I just donāt like them in Go. I love them in Rust, theyāre perfect; they work really well. But I donāt write Rust code to be readable, and I donāt have the same level of productivity in Rust that I do in Go, because I can read my Go code really quickly and figure out whatās going on.
Yeah, yeah.
I feel like I have a very rough, still-developing analogy that Iāve been kind of [unintelligible 01:07:32.20] with my friends about why I find the desire to have generics so much a bit ridiculousā¦ And it goes a little bit like this - okay, itās kind of like saying that you canāt write a good story in German, because German has gendered words and multiple version of ātheā, and English has this generic ātheā that we have, and no genders for most of our words. So it makes English a better language for storytelling because of that.
That sounds pretty inherently ridiculous, because your story as a whole has much to do with so many other things that arenāt related to the nice syntax of the language that youāre using for itā¦ And I think thatās a lot of the case with Go, where itās like āThis is a successful language, and weāve gotten this far without generics.ā And thatās not in its face a good reason to not have genericsā¦ But it also kind of tells you that maybe at the end of the day these wonāt be the things that we need or the golden feature that we really need to have to make the language even betterā¦ But I also definitely agree with Brian on the kind of cost of generics, which I feel like is not ā I feel like among experienced Go engineers itās talked about a lot, but itās not talked about a lot at large.
A lot of my code reviews - and I think Mitchell Hashimoto tweeted out about this, of like now he has a standard thing heās gonna put in his code reviews, where heās like āDoes this really need to have generics?ā And I think a lot of us are gonna spend a lot of time looking at code that has generics in it, and have to go and explain to people āWell, do you really need to have generics here? Thereās probably a better way to do it, thereās probably a cleaner way to do it.ā We donāt really need to have this, and having lots of back-and-forth conversations about generics now that itās gonna be in the languageā¦
I donāt really know how much of a benefit itās gonna add, outside of the āOkay, weāve written some container libraries that we now have to use. Great. Now we have this for generics.ā But where else is it really necessary and was causing a lot of pain? I think in the first five years of us having generics itās gonna be a big negative on the language ecosystem and Go engineers.
I did a talk at Gotham Go called āThings in Go I never useā, and it was kind of the same idea of even things we already haveā¦ And this idea that a smaller language footprint is better, so thereās things that I just happen probably never to use very often. I think itās gonna come down to that a little bit. We do have to be careful, we have to educate, because itās really people that come from other languages straight away that are gonna solve problems maybe with generics immediately, and there will be an education, I think, to make sure that weāre using it in the right wayā¦ But itās the same with code generation. You can abuse that too, and we do.
I think itās exactly like code generation and reflection and all these other things; I think channels goes in this, tooā¦ Itās like, we donāt need to use them most of the time. But when we do need to use them, they are very useful to have. But itās difficult to figure out when and why you should be using these things, and it takes a lot of time and experience, and while youāre building that time and experience, thereās a cost that you have to pay for having those things in the languageā¦ And it feels like weāre adding another big one, thatās like āOkay, now weāre just gonna have to teach a lot of people and figure out as a community how do we wind up writing Go code that still feels like the Go code that weāve had in the past, with this new feature.ā I think thatās just gonna take a lot of time and a lot of effort, and itās gonna cause a lot of stressful things for people.
What was the big Mozilla data processing library? Really early onā¦ It was one of the first big learnings that GopherCon brought for me specifically, was that channels arenāt the answer to everythingā¦ And I should remember the name of the library, but there was a big data processing library written in Go, and they used channels everywhereā¦ And it didnāt scale. Once they hit a certain threshold, channels arenāt the answer.
Yeah, itās funny ā I mean, Iām guilty of thisā¦ When I learned about channels, I loved it, because I saw some example cases where it was used brilliantlyā¦ And then I over-used it. And it wasnāt that it didnāt scale in my case, it was that it was hard to follow what was happening. And then I started just using a mutexā¦ And Iāll tell you what - just saying ālockā and āunlockā is very clear; thatās really all you need. Itās kind of perfect.
When defer used to be slow, I would even just defer the unlock sometimes. So it wasnāt really about performance for me, it was about the readability. But yeah, sometimes just a mutex is very powerful.
I guess I have mixed feelings about this, because on one hand it kind of sounds like part of the argument is āWe donāt wanna spend this effort on educationā, educating people why they shouldnāt be using generics, and the same could technically be said of channels; there are definitely a lot of cases where new Go developers need educated on why channels arenāt something you just throw in every bit of your code. And I definitely agree that thereās going to be a cost and thereās going to be opportunities for either somebody to make books or whatever that help educate people around when generics are actually a good fit, versus āOkay, you think you need generics here, but hereās actually other ways to solve this problem.ā
I think once that hopefully comes into place, there are some good solutions ā not solutions, but good resources out there to show people how to avoid genericsā¦ Then I think theyāre gonna kind of fade off into something you rarely have to mess with, but theyāre there when you really need them. I get the concerns, I guess I just donāt feel like theyāre reason alone to not add generics to the language.
Oh, interesting.
I do have ā itās not like a counter, but like another perspective on this as well, because I think itās likeā¦ Itās never kind of just like āOh, itās either we do this thing or we donāt do this thing.ā Thereās always like āWe do this thing, and weāre not doing a bunch of other thingsā, and I think thatās more of the problem for me. We have spent a huge amount of time as a community working on genericsā¦ And obviously, we canāt force people to work on things, so itās not exactly like a āWe should be working on something elseā, but I think we as a Go community and also we as an industry need to do what I was saying earlier, of like start moving away from this obsession of language features and obsession with like āOh, well, this language doesnāt have this feature, so itās not gonna be good for writing software inā, and start moving up into those higher levels of actually writing better software and using higher-level paradigms that arenāt necessarily dependent on the language itselfā¦
And I feel like Go has historically been pretty good at that, because it doesnāt have a lot of features. And the kind of extra features we do have in goroutines and channels, as weāve been discussing - we donāt use them a lot; theyāre kind of for special āOkay, I know I need to use this here.ā I think what that has done, perhaps by accident, is push people to focus more on the software itself and the thing that weāre trying to build. And I think that my worry of us ā itās not really about generics at the end of the day, itās just this more focus on like āOh, weāll just add things to the language, and that will fix our larger problemsā, when itās like, āNo, we have to get back to the how do we actually build applications so they donāt fall over? How do we build applications so theyāre readable and maintainable? How do we build applications so we can quickly onboard new people, so we can get more resources onto projects when itās necessary?ā
I think those higher-level meta things are constantly being left out of the conversation, and weāre not discussing them in enough depth, and I think generics is one of those things that just sucks more air out of that space.
Hmā¦ Yeah, I think thatās a great point. I wish we had more time to discuss generics in more detailā¦ And we probably will, on a future episode of Go Time.
Just donāt invite me. [laughter]
No, we need all the perspectives. Brian, if you leave Go because of generics, Iāve got one message for you, mateā¦ Youāll be back
Yeah, BYEā¦! Bye, Felicia.
[song excerpt 01:16:40.17]
āYouāre saying the price of generics is not a price that youāre willing to payā¦ You cry over the tears from C++, go Errorf yourselfā¦ Why so sadā¦ Remember we made an arrangement of simplicity and now youāre making me madā¦ Remember despite Goās strange syntax youāre a fanā¦ Youāll be back. Soon youāll see, youāll return for the concurrencyā¦ Youāll be back. Time will tellā¦ Because interfaces work so wellā¦ Dev speeds rise, bug counts fall, next to Java programs Go look smallā¦ And when push comes to shove, we will finally add generics to reminds you of our loveā¦ Go-Go-Go-Go-Go-Go-G-G-G-Goā¦ Go-Go-Go-Go-Go-Go-G-G-G-Goā¦ You see the spec is draining and you canāt go onā¦ Youāll be the one complaining on GitHubā¦ And no, donāt change the subjectā¦ Generics is my favorite subjectā¦ A very specific subject. An objectively uncontestable subject. Youāll have them forever, and ever, and ever and ever and everā¦ Youāll be back!ā
Can I have a little tiny unpopular opinion I can just drop and run?
Yes, please.
So my unpopular opinion is that estimating how long things are going to take is not actually that difficult. We are just so ridiculously bad at it that it seems impossible.
Okay, well whatās the difference really between those two things? If weāre all terrible at it, why donāt we just stop lying to ourselves?
I think the former is something we can fix. If it really was true that you canāt estimate things, and itās this impossible task we canāt get right, then weāre kind of stuck; you canāt ever really fix that, weāre just kind of stuck with āI donāt know, software will get done when it gets done.ā But if itās that we havenāt developed the skills necessary to be able to estimate well, thatās something we can work on. Thatās something we can fix. And I think thereās writing out there and thereās things out there, especially in other industries, that can help us understand why is it that we are so bad at estimating how long things are gonna take in softwareā¦
One of the things I always talk with my friends about is how like āI donāt know, whenās the last time you factored in peopleās vacation schedules into your sprint planning, or into your quarter planning? How many times have you actually pulled in a vacation schedule and had that be part of the way that you calculate things, and itās not just been like a background thing?ā
Yeah. Well, nevermind those things which you could factor inā¦ What about all the stuff that you learn as you start? Thatās the trouble for me - as you start and get underway on something, you unravel so much stuffā¦ So you can get good estimations, but you have to do quite a lot of work upfront to get themā¦ And then why donāt we just focus on doing the work?
Iām not against deadlines. I actually really like quite aggressive deadlines, because it really forces you to prioritize. But itās not about cramming all that scope, cramming everything into that short time period. You allow the scope to be flexible, you pick the most important, and sometimes thatās the hardest bitā¦ People want everything, of course, by the release date. But if you canāt have that because of (I donāt know) the laws of physics, then what would you prefer to have first? And that, even as a question, is a very difficult oneā¦ And sometimes the worst managers Iāve ever worked with will say āNo, itās all top-priority. Itās all top-priority.ā Because Iām saying, āWell, letās try and order this.ā āNo, we need it all. Itās as simple as that.ā
I feel like our industry has veered very much into being scared of doing anything upfront. Weāre always just like, āNo, no, weāve gotta just get into it, just get into the work.ā And I think weāve very much underestimated the actual cost of doing that, of likeā
A surprise?
ā¦now we have all of this code that we have to maintain, and if weāve got this design wrong, then weāre stuck with this codeā¦ Iāve worked on so many legacy codebases that are like āIf youād spent an extra three weeks just thinking this through, we would have had a much cleaner design and a much better thing.ā And I feel like software projects constantly are running over by months and months and months, that spending an extra 2-3 weeks upfront to sit down and really start figuring out, estimating and figuring out all the risks of what youāre trying to build is well worth itā¦ But we donāt make that decision, because thatās like the bad thing. Thatās like waterfall if we start doing that.
VCs donāt value planning. They value shipping. And crushing it. [laughter]
I agree with that.
That definitely seems like part of the issue, is the short-sightedness, I think, in generalā¦ Iāve sat down with people before and Iām like āIs there ever really a softwareāā People are always just like āOh, we can do this thing in two weeks. We can do it in a month. Weāll build this service in two weeks. Weāll build this new feature in a monthā, and Iām like āWhen has it ever actually wound up taking only two weeks, or only a month?ā Never. It always just drags on and on and on, and then itās like six months down the road and Iām like āWell, we could have actually planned a proper project and been done with a good thing, a good service, a good feature at the end of those six months. But instead, now weāre gonna drag it in for another four months, and then weāll have like half of what we really wanted to have at the end.ā So I feel like a version to planning very rarely works out in our favor in the long run.
Preach it!
Yeah, I meanā¦ There are for sure ā itās one of those āIt dependsā, I think. The thing is, quick prototypes and things like that are great for this, because they feed into that processā¦ Because youāre right, of course, if you can do the design, if you can think ahead a bit - I mean, thatās really what our intelligence is meant to be for - then we should do that. I think it depends as well whoās doing that design, and the fact that you need to have that mechanical sympathy with the system as it is today, and the changes youāre gonna makeā¦ Because without that, itās just too difficult.
So I just think it is difficult, itās very hard, but thereās definitely cases where a bit of design, a bit of thinking ahead would have just saved a lot of time; itās just very difficult to know those instances. I guess thatās where experience comes in. Great one.
I guess the way I would put it - with the limitations and the current way a lot of management works, itās almost impossible to estimate projectsā¦ But if your management is willing to work with you, to be like āI need a week to figure this thing out, to do a prototype and to figure some stuff out in designā, and then we can sit down and come up with a good estimate from that point. And if you have a team, itās not necessarily ā I guess what Iām saying is itās not just learning how to estimate; part of it is having a management team thatās willing to let you figure out what you need to figure out, and let you actually learn the skills you need to learn to estimate accuratelyā¦ And in a lot of places they just arenāt willing to do that.
So youāre expected to estimate a project without doing any prototyping, and if you come back and give a realistic number, sometimes theyāre like āNo, thatās too long. It needs to be less than that.ā And itās like, āWellā¦ā Youāre expected at that point to just give them a less number, even though itās not accurate.
What I always say, āWell, what do you wanna take out?ā And when they say āNothingā, you do have a problem.
I do think too that we canāt solve this on our own. It has to be larger ā both industry and the other aspects of the organizations weāre in all have to change to get away from this mindset of like āOh, we can just pump out this feature without actually having to do the stuff around, all this designā¦ā
I think at the end of the day it comes down to risk assessment. We have to sit down and actually assess all of the risks, and that really is not just the things that could go wrong, but all of the designs, all of the prototyping that we have to do. We have to sit down and really put all of that at the forefront, at the beginning of what weāre doing, and continually do it throughoutā¦ But as you said, we have to include management in that; we have to include both product management and people management. This has to be something that we all do together, understanding that if we start doing this, the software we build not only will likely get delivered faster, but itāll be of higher quality.
So I think thatās the other thing that happens - if we donāt do this estimation, we wonāt succeed most of the time, which I think is the case right now. We arenāt succeeding most of the time. Weāre failing most of the time. Software is so broken that weāre all used to just working our lives around it. We just refresh the page, turn it off and turn it on again. This is just how we do things, and itās like āHa-ha-ha, thatās fine.ā But itās rooted at this problem - we donāt actually know what weāre doing when we go to do it, and we just kind of wing it. In most other industries, that just doesnāt fly and just doesnāt work. Imagine if youāre building buildings and youāre like āWeāre just gonna wing it. We donāt need to sit down and come up with the blueprint and estimations of the resources this building will use. Weāll just drop it on the land and hook it up to the public utilities and hope that everything works well.ā But thatās quite honestly what we tend to do a lot of the time.
Weād have some great buildings there if we did let people do thatā¦ A lot of them would fail, but the ones that worked - imagine themā¦
Yeah, [unintelligible 01:27:16.28]
Yeah. Angelica Hill, another Go Time panelist, is actually a manager herself, so we are gonna do a future episode where we dig into this. And I want to call the episode āManagers. Do we need them?ā [laughter] And weāll find out. Weāll solve that one.
We are well over timeā¦ This has been a great conversation, but I need to do some host duties now, and basically get rid of you all. But thank you so much. Brian, maybe you could play us out after I do this final thing.
What do you wanna hear?
Oh, whatever. Anything youāve got, mate.
Whenever you invite managers on for that show, can you make sure you tell them the title, the working title?
Noā¦ [laughter] Okay, thank you so much for everyone paying attention today. Itās been an excellent episode. Kris Brandow, Jon Calhoun - always a pleasure. And of course, Brian Ketelsen, what would we do without you? Thank you very much, sir.
So how long am I gonna be blacklisted from being on Go Time after this episode?
It depends how good this performance is now.
[playing the guitar 01:28:40.29]
Ole!
This is now the after-partyā¦! Great stuff.
Iā¦ Iāve gotta go to the bathroom, manā¦ [laughter] It is timeā¦
Thatās going in. Thatās going in. [laughs] That has to go in. That was a great ending. Weāll cut the song, and then weāll just play that clip, āIāve gotta go to the bathroom, manā¦ā
Iāve gotta go to bathroom, manā¦ Weāre done.
Brilliant!
Our transcripts are open source on GitHub. Improvements are welcome. š