Go Time ā€“ Episode #170

Talkin' 'bout code generation

with Brian Ketelsen

All Episodes

O.G. Brian Ketelsen joins the panel to discuss code generation; programs that write programs. They also discuss IDLs, DSLs, overusing language features, generics, and more.

Also Brian plays his guitar. šŸ¤˜

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

šŸ“ Edit Notes

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

šŸ“ Edit Transcript

Changelog

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.

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.

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.

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.

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.

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.

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.

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!

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!

Changelog

Our transcripts are open source on GitHub. Improvements are welcome. šŸ’š

Player art
  0:00 / 0:00