In this insight-filled episode, Bill Kennedy joins Johnny and Kris to discuss best practices around the design of software in Go. Bill talks through scenarios, lessons learned, and pitfalls to avoid in both architecture and coding of Go projects.
Code-ish by Heroku – A podcast from the team at Heroku, exploring code, technology, tools, tips, and the life of the developer. Check out episode 101 for a deep dive with Cornelia Davis (CTO of Weaveworks) on cloud native, cloud native patterns, and what is really means to be a cloud native application. Subscribe on Apple Podcasts and Spotify.
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
Sourcegraph – Sourcegraph is universal code search for every developer and team. Easily search across all the code that matters to you and your organization: find example code, explore and read code, debug issues, and more. Head to info.sourcegraph.com/changelog and click the button “Try Sourcegraph now” to get started.
Many of the design philosophies discussed during the show are encapsulated in this repo.
Click here to listen along while you enjoy the transcript. 🎧
Hello and welcome to this episode of Go Time. I am your host, Johnny Boursiquot. If you haven’t heard me on the show for a little while, that’s because I’ve been taking a little bit of time, you know, smelling the roses and taking care of family and the home front… But I’m happy to be back today with a nice, meaty topic, I think, and a special guest as well, that I’ll introduce momentarily here.
Joining me today to co-host is Kris Brandow. Hello, Kris.
Hey, Johnny. How’s it going?
It goes. I’m feeling – I wouldn’t say totally refreshed, but it’s good to take some time to be grateful for things and sort of recollect yourself kind of thing… But yeah, that’s where I’m at; that’s my state of mind right now.
Speaking of state of mind, I’m very happy to have on the show today Mr. Bill Kennedy, a name that you’ll probably recognize if you’ve been in the Go community for any length of time. Bill writes a ton of blog posts, and teaches, runs workshops, manages the Go Developer Network, along with a cadre of other folks… So Bill, welcome to the show. How are you?
[04:18] Thank you for having me. After hearing that, I’m a little tired myself, Johnny. I think I’m gonna tag out; you come in and I’ll go out. [laughter]
Yeah, I’m telling you, it’s time to take stock… And today, actually, I’d like us to spend some time talking about, or rather taking stock of design philosophy. It’s a kind of nebulous topic; if you hear “philosophy”, like “What does that mean? What am I supposed to do with that? I’m an engineer, I’m a software developer.” We’re all about the precision and things, so philosophy can feel a little mushy. So today I’d like us to unpack that a little bit, and obviously contextualize that with the Go programming language, but I’m sure we’re gonna spill beyond that as well… So what I’d like to do really is – let’s frame what we mean by design philosophy. What do you mean by that, Bill?
So the idea of design philosophy came to me as I was learning Go, and started studying lots of other people that came before me in terms of their thoughts and opinions on how to write software. I collected all of these quotes – I have a document that I tend to share every once in a while on Twitter. We’ll get that in the show notes. I kind of gathered all of these different thoughts and ideas and integrated them all together into my own design philosophy on how to engineer things.
For me, design philosophy is actually something that’s very concrete, it’s opinionated, and it’s really based on a collective of ideas that I think work together. I also think it is so important when you’re engineering to have a design philosophy for everything, because that’s what you’re gonna hit up against when you start to have the engineering conversations around guidelines and idioms. But when you’re lost and you’re not sure what the right answer is, I always fall back to “Well, what are our design philosophies here? Are we violating any of them? Which ones keep us more in line with the way we wanna do things, as opposed to not?”
Is a design philosophy the same thing as architecture, or like planning software? Are these things synonymous, or are you thinking something different?
Let me give you a small example. I’ll give you my two foundational design philosophies. These are in the basement, they set the foundation for everything. When you don’t have anything more specific to lean on, you lean on one of these two things. Number one, we don’t make things easy to do, we make things easy to understand. That’s a design philosophy. We can bang that up against micro-level decisions, we can bang that up against macro-level… When I say “macro”, I mean architecture, and things like that. So we always focus on easy to understand, not easy to do.
And the second one is really about precision. Everything we do, every encapsulation that we define, whether that’s a type, or a function - everything we do must define a new semantic, some new behavior where one is absolutely precise. And this idea of precision-based semantics for me drives a lot of the decisions that we’ll make.
I had a conversation with my business partner today, and you’ll hear me talk all the time, “Functions over methods, functions over methods.” Why? Because a function can always be more precise than a method. And when you have the opportunity to use the function and we’re very clear about what the parameter list is, and the return argument, and you’re being precise - well, my design philosophy says “Let’s go functions over methods” every single time. That’s at a micro-level.
At a macro-level for me it’s about layering. Can we be precise around what layers of code we’re gonna have in this project, so we can maintain a mental model around these things? So these are base design philosophies, but then we can add to it.
[08:21] In that document that we’ll share there was a study that basically said – two studies, over 30 years or something, that basically said “Nobody can maintain more than five things in their head at any given time.” So now I have a design philosophy around the idea that when we’re layering things and we’re doing things, let’s try to keep it at around three, because then we know that we can keep it in our head. So my project structures have three layers. And if you want layers inside of that, they really can’t be more than three. If you get to five, that’s fine; if you get over five, we know what’s about to happen.
So the design philosophies are there to guide your decision-making, in the guidelines, in the structure. And then allows you doing code review. I catch things in code review because my brain goes “Ben, we’re failing a design philosophy here.” “Why? How? What are we doing?” And you can start to smell decisions you’re making just from that alone.
Kris, I’m curious to get your opinion as well - what do you think of, where does your mind go when you hear design philosophy?
I’ve actually been thinking about this in the past couple days, and I kind of feel like it’s a high-level umbrella term for me when it is applied to software engineering. So I feel like, as Bill was saying, there’s multiple layers to it, and I feel like at one layer there is this software design, and then at another layer there’s code design. And I feel like those are two different things in my mind, and I feel like – I don’t know, I feel like the principles of each in some ways pull against each other, but in some ways they are collaborative. And I guess I’ll extrapolate on that a little bit.
So when I think of software design, I think of timeless, higher-level things. It doesn’t matter what programming language you’re using, if I pull from distributed systems I’ll say things like the CAP theorem, or using specific levels of consistency for specific types of problems you wanna solve. You don’t have to write in Go to have a linearizable system. You can write in any language.
When I think about design, I think about the tension of that, compared to the tension of us wanting to write – it sounds like high-quality, lower-level code. Or that we have this design philosophy of like “Okay, well we’ve decided the system fits well with linearizability, but we’re now using a language that is more difficult to use linearizability correctly in.” So should we be using it there, or should we be trying to maybe change the high-level design that we’re doing so that it fits better with the code design-level things that we wanna do?
So I feel like design philosophy is fundamentally about those types of trade-offs, at both the code and the software level, and thinking rather deeply about those types of trade-offs and coming up with nuanced solutions to them. And I definitely agree with Bill, and I think that keeping things to small buckets is best. You don’t want to have people look at something and have to memorize seven things, or remember seven things or nine things or ten things. It’s not going to work out very well. And I think that in order to do that, you have to start with something much larger and distill down over time.
I think a core part of design philosophy for me is just start with kind of flailing around, figuring out what is the thing you’re trying to build, and then distill it down. And once you’ve distilled it down into “Okay, here are my three points. Here are my four points”, then you’ve actually got something that you can then progress forward. That was a very jumbled answer, so…
[12:02] No, I wanna tie that back into what Bill was saying… I hear those definitions or those takes on design philosophy and I can’t help but notice that there’s no precision to it. You can’t lint for that. Bill was talking about like a code smell earlier, in a pull request. You’re looking at that code and you’re like “Well, that goes contrary to our philosophy.” So it’s something that seems like you kind of have to get used to, to designing a certain way, get used to layering your systems a certain way. And if you work on Bill’s team, that team’s design philosophy is going to be different perhaps from my team, or your team, Kris.
So it doesn’t seem like it’s something that’s universal. It’s more like a “This is what this person/team/company, this is what they believe works best for them. That is their approach to design, that is their approach to building systems.” Am I missing the point here, Bill?
I’d take it even down to the project. For me, every repo of code is a project. This is why I get nervous when every developer has their own repo of code, because every repo or project represents the philosophies and policies that that code has to follow. So I’m always in a less-is-more – “Can we put more developers and put more things in this one repo or project?” Because then we’re gonna have a more consistent policy set for more of the code that we’re working on.
To me, philosophy and policy kind of come into play. I have three layers for a project structure, the last layer being – I call it the foundation layer. I can think of it as like the standard library for the project. It’s code that needs to be as highly reusable and potentially code that can be reused in other projects, even though it belongs here. Now, what we can do is set design philosophy and policies around this layer.
One policy I have on code that sits inside a foundation is that it is code that’s not allowed to log. I see you pass a logger to any package in the foundation layer, code review is gonna stop. We set this strong policy; logging is a business concern, because the project gets to decide what logger they need, whether it’s structured or not, what the purpose is… That’s not foundational code. That’s not code that can be reused across projects. So for me, the design philosophy is setting the policies at a macro level sometimes - what code can do at a certain level/layer. Or it defines the syntax we’re gonna use.
Here’s something else, at a micro-level of code. I have really a guideline that says “If you’re constructing a value to a variable, you’re not to use pointer semantic construction.” I don’t wanna see the use of the ampersand operator on a construction that’s being assigned to a variable, mainly because construction in Go doesn’t tell you anything about where the value is going to be placed in memory. Escape analysis, how it’s shared. So the ampersand operator for me is one of the most important micro-level operators we have to readability; because if I see return &u I know “Well, there it is.” I can look at that one line of code and know exactly what the cost is. Readability is about understanding the cost of things. But if I move that ampersand to the construction call, and I just say return u how much readability did I just lose?
So my design philosophy is that when we talk about readability, there’s two aspects of it. A subjective one and a measurable one, the measurable one being that every line of code should not hide the cost of what it’s doing. That’s a design philosophy. Every line of code should not hide the cost of what it is. Because as soon as you start to hide the cost, you can’t look at a line of code and make an engineering decision. If you understand the cost, then you’re engineering.
[16:07] So now we apply it to a guideline - don’t do pointer semantic construction to a variable, because you’re moving the ampersand at the top of the function, when it should be somewhere else. And now somebody says, “Well, Bill, what do you want me to type, &u four times because I’ve got APIs where I’ve got to share that?” And I say “Absolutely, I want you to have &u four times”, because it’s not about making things easy to do, it’s about making things easy to understand. Boom.
So you see how all of these philosophies kick in at different levels from a micro and a macro level? And I’ve studied for a long time the thoughts of a lot of people to integrate all these ideas together. And a lot of the people that I’ve studied tended to be in and around, I’d say, Rob Pike and Rob Griesemer at Bell Labs, other language designers.
So I always felt like there was this common theme among a lot of these people that I also kind of see in Go and then can apply to Go. But hopefully that gives you another kind of idea of where things are going. So when I see a particular syntax and I see I’m not able to understand the cost of something, it starts to trickle in my brain, “Okay, we need to refactor something here, to get back to these ideas.”
In some cases there are some hard and fast rules that you can apply, and things that you can look for like having & in front of something, as opposed to having it where you actually declare the thing; that’s one hard and fast rule. I’m wondering, what are some other hard and fast rules that you have syntactically in Go code that are part of your design philosophy?
I have a whole set of philosophies around the idea that you start programming, in the sense that you’re trying to get something to work. You always are in a programming mode at the beginning. “Let me just find some code; I don’t care what it looks like. Let me get something to work.” And then, for me, you have to move into an engineering frame of mind, even if it’s prototype work. Some of the most dangerous programming I see is the code that somebody does and hands it off to somebody else, thinking that they’re going to now do the engineering piece. And they don’t. And that code ends up in production.
So there’s a bunch of philosophies around moving from programming to engineering. Look, Wes Dyer said this, “Make it correct. Make it clear. Make it concise. Make it fast.” Those are four steps. To me, those are four different refactorings that you have to do to a piece of code.
In that order?
In Wes’ world it’s in that order. And I agree with Wes. But there’s other refactorings you should be doing for code. Make it precise. He might be saying “Make it concise”, and I think he means precision there. Can we make the API cleaner? Can we make the API more precise in terms of what we need?
And then to me there’s a piece that is lost, too. There’s a refactoring piece about making something testable. And I think sometimes people start with the idea that this is gonna be testable. No. What does it mean for something – it’s a refactoring piece. I’ve worked on a function for three hours with somebody, where we’ve made it correct, we’ve made it clear, we’ve made it concise, and then they thought “Wow, this is great.” I said, “Yeah, this is great, but it’s not testable yet.” We need to refactor this to make this testable. We need to think about how we can do that without having to add an interface, without having to add an abstraction, without having to do some of the things that we kind of–
[19:50] So a design philosophy for me is this - you write things in the concrete first, then you ask “Does that need to be polymorphic? Does that require an abstraction layer?” In other words, can that function continue to ask for data based on what it is? Is there a reason why it needs to ask for data based on what it can do?” And I think too many of us default to the idea that everything should be asking based on what it can do, and I say “No, that’s an engineering cost.” First of all, it’s an allocation out of the box. And second, it’s an abstraction. It doesn’t necessarily make our lives better. So it’s a stage, and sometimes the answer is “Yes, this is a situation where we should be decoupling the code.” And there’s times where it’s like “No.” We only have one implementation of this, so let’s not do something for the sake of doing it. That’s another design philosophy. Let’s not do something for the sake of doing it. Let’s not make things polymorphic for the sake of leveraging interfaces. Let’s refactor that.
And I’m always talking about complexity cost. Don’t make something complex until you absolutely have no choice. That’s a design philosophy. If something can be single-threaded and it’s fast enough, why the heck do you wanna make it multi-threaded. That’s an order of magnitude more complexity. And I think sometimes people feel pressed to show that they can do these things, or they’re bored and they wanna do these things, or they’re being asked to do these things without any real engineering understanding behind it.
So these are philosophies where somebody comes up and they’re like “Okay, we’re gonna start throwing channels at this…” and I’m like “Just slow down.” Let’s not add the complexity until we absolutely need it. Prove to me that we need it.
Your approach to designing software then is – you might have some core fundamentals that transcend a particular project, but you’re gonna be taking a fresh look and approach to every project. In other words, where I’m going with this is that you have philosophies that are part and parcel to using a framework, or using some APIs, or using a particular set of packages and whatnot. These things have encoded the philosophy of the designers of those common pieces, that you can take from project to project.
So if you’re starting a project today and you know the last 2-3 projects that you used, you used a particular framework that makes life easy - and I can kind of guess where you’re gonna take me with this, but it makes things easy to get going and to see results right away… Like, are we doing it wrong for wanting to sort of run to the things we know and perhaps love, and the things that make it easy to get going, without thinking of a new about a problem that we’re trying to solve? Where does having a design philosophy force you to sit down and think of everything fresh, or can you take a shortcut and reuse things that have worked in the past?
I think you need to reuse the things that are already bound within your design philosophies that you have. You’re not gonna write everything from scratch. I have a repo out there called Service. I teach people from beginning to end “How do we write production-level services in Go?” We basically pair-program that from beginning to end. And the entire five days is me saying “Why?” Why I’m doing something. I think that’s the key. Your design philosophies and guidelines come out of the Why. I think comments in Go should be the Why, when the Why is not obvious.
[24:24] I published that Service repo project specifically for it to not be a framework, but to be what I call a starter kit. Because I feel like frameworks can add value like you’re saying, but you’re locked into the walls related to that framework, because you’re not really coding at that point, as much as you’re plug-and-playing inside whatever those endpoints are that they give you. I didn’t want that. I didn’t want another framework. I want people to be able to engineer from the ground up; not necessarily they have to start from the ground up, but be able to if they have to, get back down into those layers. That’s what the starter kit is about.
So I teach from beginning to end every package, every layer, why we’re doing it, the constraints that I have that you may not. You’re gonna have different constraints that are gonna require you to have different engineering choices… Which doesn’t mean that you’re violating a design philosophy, as much as you now have to – I’ll give you a perfect example. My first version of the business layer for this project user package, products package, your entity type packages, reusing functions I had this conversation with Ed today; I think it’s a classic example of where multiple people can do different things and still be bound to the same design philosophies and guidelines. It’s all about where you are in a project.
So I first had functions. Now, at that time, every function needed a database connection, because we have a design philosophy that at the business layer the context should be empty, and those functions should work. If you’ve got business-level packages in code that require something in the context to work, you’re in a lot of trouble. That design philosophy for me is a critical one.
So we’re not gonna hide database connections in a context at the business layer API. You’re assuming that then only web-based apps are using the business layer. No. I could write a CLI tool, I could write a UI tool today. And now they don’t know that they’re supposed to stick database connections in – like, it doesn’t work.
So the original design of a function in the business layer was context, because that should come first, database connection, and then whatever two or three other things you needed in order to execute a query against the database, if it was a create or an update.
So about three weeks ago I decided that I wanted to start logging the SQL query that was executing. Now, if you’re able to do that, if it’s not a privacy violation, which it could be in some shops, is when we talk about different constraints. I have always loved logging the SQL queries, because if there was a bug, I could just copy and paste it out of the log and I’m not wasting 10,15, 20 minutes trying to reconstruct it. And now you’ve gotta be smart about which ones you’re logging, but minus that, I wanna start logging.
So now, if I wanna start logging the SQL query, I need to pass a logger in. So I start passing a logger in, with a database connection on the function signature. Highly precision. If we talk about precision, this is precise. Some functions have 7 or 8 parameters at this point, but I don’t necessarily mind that because of the precision; but I’m sitting back and I’m looking at this API of about eight functions, and I start to realize that passing the log and the db in, which is mandatory across this entire API set, is no longer giving me the precision I want. Those two parameters are starting to be noise, because technically, even though I need them, it’s not really relevant to performing a create or an update. And my brain said “Even though it’s precise in terms of parameter list, these two parameters are starting to create noise for me.”
[28:30] So what did I do? I said “Let me prototype the idea of turning these into methods”, since the log and the database connection is instantiated one time; we don’t need to instantiate it on every request. And I could also move these into methods and a value of this type only has to be instantiated once, at the beginning of a program… What I could do is move the database and the log as a receiver access and make the APIs precise again, and get rid of the noise, which I did, and for me it worked out. But then Ed looked at that and he says “Bill, you’re breaking your design philosophy around precision. Isn’t it more precise if you pass it in?” I said “Yes, let’s just say it is, except - look at the entire API set. Look how this is kind of noise.” We only instantiate all of this one time. If I had to instantiate this for every single request, the answer would be “Nope. Sorry. Gotta live with it. We’re passing it in.” But because the type bound to the method set only has to be constructed once at startup… Now I just reduced the noise out of the API, and from my perspective, I think I’m maintaining better precision.
Now, that’s subjective, but we can all have that engineering conversation, and that’s what I want our teams to be having. Those are the best ones. Let’s debate if this is more precise than that. And then we’ll make a decision. This is where I’m going with it; this is why you need design philosophy, because if not, you’re not making the best decisions.
Now the conversation we’re having is subjective, but it’s objective to the rule of precision, and we can all kind of weigh in on what we think it is. Ed and I both agree that probably what I’m doing in my scenario, because I now want logging, was more precise. But if I wasn’t gonna log, like I wasn’t earlier, I wouldn’t do that; I would just pass a db connection in. You see where I’m going.
But when I’m working with teams or I’m working with teams, or I’m working with people at Ardan and they come up with questions about “Bill, should I do this or should I do that?” I always start every conversation with “Okay, let’s list out the design philosophies that we think apply to what you’re trying to do”, and that always seems to do two things. One, it helps guide the conversation we’re having, and then everybody kind of just comes together and says “Okay, based on that, this is the best approach.” I just find it really works also in a team environment to have that.
This actually lines up quite nicely with a question we got on the Go Time Slack channel… “Should I design for the problem I have to solve today, or for the harder problems I might have to solve tomorrow?” This is [unintelligible 00:31:10.07] on Slack.
So here’s more design philosophy, okay? Tomorrow is not promised, so you should be writing code that you need today, not tomorrow. I cannot tell you, unfortunately, the handful of times in my life where I’ve walked into work and was told they just let go of the CTO. “Everybody stop what you’re doing. If your product is already in production, maintain it. If not, you’re done, until the new CTO comes in and decides what she wants to do.” And now you’re in limbo for two weeks.
[31:45] Then they hire the new CTO and now you lose another month. And most likely, if your product is not in production, it’s done, it’s over with. And so, from a design philosophy, I’m always about the idea of “Let’s just write the code we need now, and let’s get this into production.” You know that term “technical debt”? You know what it means to me? Everybody has a different meaning, but I can tie it to design philosophy [unintelligible 00:32:10.06] Technical debt to me is every day a piece of code is sitting on your laptop and not in production - that’s technical debt. I am hiring you to solve a problem. That problem is not being solved on your desk, that problem is being solved in production. So if you’re not getting that code in production, what am I paying you for?
I own my own business and all that, but I think sometimes people forget… You’re being paid to generate or help to generate revenue for the company, so I can pay you at some point. Even if you’re not directly related to revenue, you have to be somehow related… You just can’t be a cost center your whole life. Somebody’s gotta look at you and say “Okay, even though I can’t directly relate it to the last dollar we made, their time is helping maybe somebody else.” So you’ve gotta be conscious to the fact that you’re being paid to solve problems, problems are solved in production; whatever that production environment is for you, there is one. And so you need to be just writing the code you need today, including the abstractions you need today, because tomorrow is not promised, at any level. And you adding more code because you think this is gonna come - it may never. Businesses shift all the time.
And what’s funny about that and this question actually comes from this idea that you should be writing less code anyway. So NASA did a study - some people may have heard me say this before… NASA did a study of all of the software that they wrote. Now, you know, they’re writing pretty technical, C-level kind of code, but minus that… Their study came up with the idea that the industry average number of bugs is about 15 to 50 bugs per thousand lines of code. So I equate that to basically – and I want people to put this math in their head, because I do. Every 20 lines of code you write, there’s a bug in your software, whether it shows up or not. So if you wanna write less bugs, the math is simple. Write less code.
Write less software. [laughter]
Now, when we talk about Go - one of Go’s language design philosophies was being able to do more with less code. It’s coming from these – I believe. I can’t speak for Robert, and all… But I believe it’s coming from these ideas that if there’s less code you need to write, there’s going to be less bugs. Now, why did they say this? Because Stroustrup says “If you’re writing more code than you need, it results in ugly, large, and slow code”, where ugly means you’re leaving places for bugs to hide, large means you’re ensuring incomplete test coverage, and slow means you now start to make shortcuts and dirty tricks away from your frameworks and your patterns, because you’re moving fast and the code gets out of control… And these things absolutely happen.
So I think we’re talking about all of this… It all works together, and I think Go is tied into that. And we complain about Go’s error handling. We love to complain about it. But do you know that there was a study done where they looked at 48 critical failures that brought down systems. Hundreds of bugs in Cassandra, HBase, MapReduce, Redis… How many systems run on Redis today? And they’ve found in this study that out of those 48 critical failures, 92% of them could have been avoided if error handling was done better. Failures from bad error handling.
So again, I think that Go designers knew this. They knew this, because they were developers themselves. They were not necessarily academics. They had to build software. They knew what the average developer needed, they knew where they were falling down. And I think Go comes in and solves these things.
[36:15] Personally, I think when somebody complains about error handling in Go, they’re complaining about – they want it easier to do, not easier to understand. [laughter] We come back again, right? So sometimes when you make things easier to understand, things have to be a little more tedious.
But here’s another design philosophy, Johnny? Two of them. One, you shouldn’t be writing code for yourself. You should be writing code for the next person that has to come along. Because if you don’t, if you’re not thinking about the next person and/or the average developer on your team, when you leave, that codebase leaves with you. It gets replaced. And the 3, 4, 5 years you spent on that ends up resulting in meaning nothing. I’ve got code that’s 20-something years old, 10-something years old in production right now. The 20-year-old code should go, because that’s way too long… But I think it’s there because I always wrote code with the understanding that somebody else has to be able to maintain this. It wasn’t about me, it was about the next person, and that allows that code to now not just have to be replaced, right? You need to have that design philosophy in your head; you need to be thinking about that, “Who’s the next person that’s gonna come along here?” And then you’re always writing code for the average developer on your team.
If you’re the average developer on your team, that means I can wake you up at 3 in the morning (God forbid) if I have to, and you can handle the bug. That’s the average developer. If I can’t wake you up at three in the morning, then you’re below average. So another question is “Why are you below average? Is it because I’m failing you, or are you just not coming up to speed?” And then for me, the next thing is the above-average developer. That’s scary, because those are the developers that tend to get bored, and instead of being able to write for the average developer, or bring the team up - that’s where the clever code comes in. That’s where we trip up.
And I tell people all the time, “When you’re hiring, evaluate who this person is for your team. Are they below, are they average, or are they above?” And consciously understand what you’re gonna need to do as an individual and a team to get this person in the right place. If they’re below average – which is great; let’s hire developers who are below average for our teams, so we can bring them up and we can create a stronger team. Those are the best developers in the world, because you can really teach and train them. And now you’ve got somebody who will stay a long time and really work hard and thank you for the opportunity.
But if you put me on a team that’s doing business APIs, I’m above average. If you put me on a team doing crypto, I’m below average. And if I wanted to learn crypto and you gave me that chance, I would be ecstatic, and I’d work hard, and we’d get there. But if you’re hiring somebody who’s above - and I’ve done it before - they can either be amazing mentors and coaches, which is why you’re hiring them, I hope, or they can create utter chaos and destruction, because everything they’re doing is not comprehensible to anybody else on the team, and you’ve gotta maintain it.
[39:40] So those are design philosophies around building teams, around the ideas of all of this stuff. And you wanna apply it back to micro-level decisions, like constructions, functions versus methods, to macro-level decisions around app layer, business layer, foundation layers of code. Policies for these. Import policies. Error handling policies. Who can shut down an application? Who can’t? Who can log? Who can’t? Who can wrap errors? Who can’t? Who can set certain import dependencies? Who can’t?
And you don’t have to have all of it day one. You have to develop it as “Suddenly, there’s a hole in the engineering decisions. Hm. We don’t’ know what to do here. Okay, that means we may not have a design philosophy here.” I get excited when that happens. I’m like, “Oh my God, we’re gonna have a design philosophy for this. Oh my God, we get to do something new! WOOOH!”
Now, you’ve always got some of your base, foundational, right? But those are exciting days. And it’s also exciting sometimes when somebody finds a hole in a design philosophy or policy, where we thought this was the right thing to do, and suddenly we’ve found an exception. And there’s exceptions to everything. There are some exceptions you just can’t take. I don’t really take exceptions between project layers. I’ll never let the foundational layer log. There’s no exception to that. If you have to log, you’re in the business. That’s it.
But then there are other exceptions… Here’s a good one, Johnny. Here’s one where you might take an exception. So baseline design philosophy - a type system is not to be shared. A type system exists to allow package, which is a unit of code in Go, a clearly compile-time unit of code. A type system is design to allow data to flow in and out of the package API, where a package has a purpose. So if the type system’s job is to allow data - if. There’s my philosophy. If you agree with this. You don’t have to agree with anything I’m saying today, by the way… It is totally fair. But if you believe that a type system’s job is to allow data to flow in and out of a package, then that type system is highly localized to that package and that package only. So now you have to make a decision about every API. When it comes to data flowing into an API, you have two choices. You could say “I want the API to accept data based on what it is.” This is what I would call concrete functions, accepting a concrete type. It can accept a user, and only a user. That’s what it is. But thanks to interfaces, we can write polymorphic functions let’s say “No, this API will accept concrete data based on what it can do.” And that’s a next level of refactoring, hopefully; I don’t wanna start there, but suddenly you realize “Not only can I work with a user, I can also work with a customer. Based on this common behavior, we make it polymorphic.” Okay. We all agree with that. You have both choices, and those are the only two choices you have. And those types should exist as types within the scope of that package.
Now, here’s where the fun begins… I have a strong rule that functions should only return concrete values. The function’s job is not to pre-decouple or wrap concrete data already in an interface; that is not the API’s responsibility. It is the caller’s responsibility to decide whether or not they need the decoupling or not. Not mine.
So minus the error interface, which is a whole another set of interesting design philosophies and things I have, I don’t wanna see a function that uses http.handler as the return type. I don’t care if you know or think they’re gonna put it into a handler already. I don’t care, it’s not my job. My job is to give them the concrete value that they can then do with what they want.
“Well, Bill, then we’re leaking a –” No, you’re not leaking a type. They already imported your package. There’s no leaking there, what are you talking about? Stop trying to abstract for the caller. Let them do it. Now, there are two exceptions to this. One is the error interface; we’re handling errors in a decoupled state. There’s lots of reasons why we wanna do that.
[44:04] And until 1.18 comes out, there are times where you might need the empty interface. It should be a little bit of a smell, but let’s be real, I’ve had to write a function or two over the last six years where I was trying to be, for whatever good reason, generic. Maybe I was just doing some data flow… And we were using the empty interface, which - now in 1.18 we’ll be able to replace with a concrete type. [laughter] I mean, what is generics at the end of the day anyway? Generics is concrete, polymorphic functions, where the polymorphism isn’t happening at runtime, the polymorphism is happening at compile time. We’re choosing the concrete type, the data – because the only data that flows is concrete data anyway. We’re just choosing that at compile time. For me, it’s concrete polymorphism, as opposed to runtime polymorphism.
But there’s a philosophy - we shouldn’t be using the interface as a return type, minus those two exceptions, when they happen. And people disagree with me there, but… There it is. So if I see a function that’s returning an interface, it’s immediately code review style, so “What are we doing? Why are we doing this? Prove to me that we need to take an exception”, but it’s gonna be hard, because if I return the concrete type, that doesn’t prevent the caller from doing whatever it is they’re doing.
I feel like there’s this kind of – this high-level or this overarching theme of kind of shifting focus away from the code so much, and shifting it more toward the software and the process. I guess what I mean by that is earlier in the conversation we were talking about frameworks and how useful frameworks can be for getting started, or for building something that you might not know how to build the intricacies of… But I think an important thing that Bill mentioned is that you can only use a framework to get so far… And at a point, you start hitting up against the friction of using a framework, instead of using it work around it or do things differently and if you don’t have that base level of knowledge in how that framework is constructed, then you can’t go and implement what you need now. You don’t have that knowledge, so you have to go acquire that knowledge.
What I feel like a lot of this conversation has been kind of hinting at is that we need to be perhaps a bit better at focusing on acquiring that knowledge earlier rather than later, because it feels like a lot of these design philosophies are answering questions that we have to help ensure that we’re all on the same page, and for the team that we’re all on the same page, that we are sharing the same amount of knowledge. And I feel like frameworks and the like of those things are shortcuts around having to do that process upfront. If you go and use a framework that has specific opinions or specific ways it wants you to do things, you don’t have to have that conversation with your team, and now you can just move on and write the code and ship the code.
But I feel like this design philosophy conversation is pushing us to say “Don’t be so focused on shipping the code. That isn’t necessarily the most important thing at the end of the day.” It’s important that we get the software into production, but that is different than just writing the code or creating the code, and how quickly you can recreate that code. We should be focused on making sure that we’re writing the correct code, for whatever correct means. Correct might mean a smaller amount of code than we might have written otherwise, or it might mean code with fewer bugs by virtue of it being smaller.
I kind of feel like there’s this tension between having good develop design philosophies and using frameworks as a crutch. I feel like that’s what happens a lot, and I wanna extrapolate on that a little bit more too, and I wanna say, it feels like once those frameworks get out into the community as a whole, once those become established things, people start forgetting completely the fundamentals of those frameworks, and it becomes a kind of knowledge that is sacred, that only a few small people are allowed to know and use and practice, and I feel like a good example of that is HTTP frameworks.
[48:08] I’ve seen throughout my career a lot of people not know the history of how certain things that we do develop, for instance, clean URLs - how they developed, or why we use them. And since they don’t know that, they’re like “Oh, we’re just gonna apply these to APIs”, and that’s how we got this wildcard thing that we keep doing in every framework because “We’re gonna support wildcards; if you don’t support wildcards, you’re not doing it well.” And I’ve always kind of sat and thought, and I’m like “But there’s query parameters that are in most cases better than wildcards. You can do more. They’re more flexible. Why don’t people use them?
And what I’ve come across is that people don’t know how to design an API to use query parameters as well because they’ve been using wildcards for so long, and they’ve been so attached to these clean URLs that people love using. And I feel like that is a design philosophy misstep. That is we didn’t think about why we would want to have clean URLs and what the applicability of them is. Because clean URLs are absolutely applicable in a browser. You want things that are easy and hackable for your users. Like, “Oh, I wanna see all of the articles for this month, and I’m looking at one that has the date. I can just chop off the day and then it’ll give me for the whole month. I can chop off the month and it’ll give me for the whole year.” These nice, hackable URLs that are kind of a handle for people to be able to use your website. But that isn’t applicable to APIs. You don’t have people – like, maybe when you’re just experimenting with an API, you’re hacking around with it, but that’s not the main use case of the API… And I feel like when we institutionalize the knowledge of things like that - and I feel like it was probably accidental at the end of the day… I feel like at some point people were like, “Oh, it’s easier to get started if we have nice-looking API URLs that I can just hack and get some data back, that I can just play around with. I don’t have to go write a little client library, or do all this other stuff.” It feels like something that was innocent that got in, and then because we all rely on frameworks so heavily, we wound up getting stuck with it, and that becoming the thing that we should all do, without actually going back and thinking about it.
Well, let’s be fair for a second… Here’s another design philosophy. Consistency in everything we do is paramount. For me, that’s where we win or lose the game a lot of times in code. The consistency. And I think frameworks start out with the idea that we’re gonna create a consistent way to do things, so we have that. But eventually, they get too big, they get too strong, and then you’re stuck. I think that’s what happens.
If you look at my starter kit, we do have a small framework. But it’s super-tiny. We’re never going to be putting up really large walls around it, because we want that flexibility. But you need some framework and some patterns for the consistency of that. So there’s a balance there. There’s a balance there that you need.
But Jaana said this one day… She said it on Twitter and I wrote it down; and again, it’s the same. A good API is not just easy to use, but it’s also hard to misuse. So the framework is giving you maybe your macro-level design philosophies kind of concreted in, but you still have to write business logic even if you’re using a framework. You still have to write handlers, you still have to write code. So maybe you don’t get to apply your own macro-level design philosophies around everything; you still have to have your micro-level – looking at an API and going “Okay, it’s easy to use, but look at all the mistakes somebody can make with it as well.” How much nicer is it if you wrote a function and I looked at it and I said, “Look, an API is just not easy to use, it also has to be hard to misuse.” You could appreciate that, because your brain can then start looking at all the engineering around that.
And then I could say “Chris, go back and think about that and see what kind of” – instead of me telling you… A good design philosophy should not result in me now telling you what to do; it should result in you going back to your desk and being able to say “Okay, how do I reduce the misuse of fraud in this API?” And then you come back. And now we’re having that engineering conversation and we can play off each other there. That’s cool.
I saw this tweet one day and I was pulling my hair out, because I just don’t think it’s fair. These types of things – I don’t think it’s fair. Somebody tweeted, “If you’re not using structured logging, you’re doing it wrong.” But that’s not a philosophy. For me, I always teach “You shouldn’t start a project until you can answer this question - what is the purpose of the logs?” What is the purpose? We all have different purposes.
Here’s a core category - do the logs contain data or not? If the logs are not gonna contain data, and they’re only used for being able to debug the application, you don’t need structured logging. You need logs that you can read, that have a bare minimal trace, and a lot of context for error handling. But if you’re gonna put data in the logs – this is where things get fun… “No, no, we’re gonna put data in the logs.” Fair. Choose a format. But if suddenly in this service you can’t log, for whatever reason, does the service stop? If you can’t log because you’re logging data, and now suddenly the log is blocking, do you tell millions of people they can’t watch that video anymore until the logging works again? if the answer is no, then you shouldn’t be putting data in the logs, because unfortunately, if you’re putting data there, you’ve gotta log, right?
You see where we’re going? Now we’re having a conversation around, “Okay, so what is our philosophy around logging? What is our logs’ purpose? We’re gonna make sure that we’re using the right one.” And then it gets to “Who can log? Who can’t log? How do we know that somebody shouldn’t log?” And this is where precision comes in for me, because I will say “We can’t use a singleton. Not even the one in the standard library. You have to pass a logger everywhere in the app that you wanna log. Why?” So when I look at that PR and I see you changed the function signature or you changed that type to now take a logger, I can pause and go “Hm… I see now they want to log… Are we even allowed to log in this layer? Oh, we are? Okay. What are they logging? Is that signal? Is that noise? What’s going on?” I don’t believe in logging levels. I have never in 30 years been able to turn a logging level up fast enough in production for it to be valuable to me. So either you need to log or you don’t. Either you’re debugging at your desk every day or you’re not.
These are just a whole set of philosophies that allow me to now make engineering decisions about what we’re gonna do. If my philosophy is “You’re not allowed to use a debugger to debug this app unless you’re stuck for 20 minutes, you can only use what you have available to you in production”, well, you’re gonna make sure that it’s working for you at your desk, whatever you have available to you when it’s in production. And when it’s not working and you raise your hand and you say “I need help”, we’re gonna figure out where it broke down. Again, that’s to me a design philosophy. We don’t use debuggers to fix bugs. We just don’t. We use them to trace through code at times when it’s code we don’t know, and there’s no real clear mental model for this codebase. But we’re not gonna use them to fix bugs.
Disagree with me. That’s fair. But that now drives a whole bunch of decisions around logging, and metrics, and what we’re gonna deploy and not deploy in terms of debugging and maintaining an app.
[55:54] I feel like a lot of the time the conversations around these things too become like not what you were talking about, like the actual “Why should we be logging? Why should we have these metrics? Why should we have distributed tracing?”, but it comes down to “Oh right, we’ve decided that we want to have this. Now, what of these 15 libraries that are available should we be using?” and we don’t even start the conversation in the right arena or the right area. I think that’s kind of what I was trying to say before too, which is like - I think frameworks are a necessary thing, they’re useful, but I think that if you don’t have the design discussions around them and the design philosophy around them, they become more of a burden than a help.
So… Quickly, before we switch to unpopular opinions… Bill. Generics. [laughs]
I’m a fan of generics. I think that generics are gonna bring some really great things to the language, that we don’t have today, that I’d like to see. Now you can say “Bill, what is that?” I wanna see a package in the standard library that can implement as many of the concurrency patterns that we all have to code ourselves. I think there’s more bugs in Go code today because everybody’s writing their own pooling patterns, fan-outs, other complex things that could be coded by somebody on the language team where you just pass a function or something, and you know that the concurrency pattern is solid. So I’m super excited about that.
The sync.Map - look at the comments around the sync.Map type. You know somebody engineered that to be mechanically sympathetic with the hardware caching system, that you don’t get if you use a regular Go map? Imagine we could put a concrete type to that. I wouldn’t use a regular Go map ever again, because if I’m gonna be doing heavy, heavy map stuff, and I’m gonna get the mechanical sympathies of the caching system with that type, and I get to use a concrete type on top of that? WOOH!
I think you’re golden. Not a lot of us build container types. I’m not really doing linked lists, and we really shouldn’t be; we should be using the slice, yadda-yadda-yadda. But I think from a concurrency standpoint there’s some really special things… And in the cases where you are using an empty interface as the return type, because you are doing something that is legit, that is reasonable, you’re solving a particular problem and then you have these empty interfaces on the return - I think there’s home runs that we can hit with the generics.
Now, remember, this is the first release in 1.18. If you look at what’s been admitted, at least, there’s still a long list, like compiler templates and things like that. I expect more over the course of 2-3 years to be added to generics, but I don’t ever expect to have what you’re seeing, say C++, or something.
But we do also want to – and I say “we”. I shouldn’t say “we”. But as a community, I think we should want more people writing code in Go. And if we bring in more of the larger enterprise companies that are using C# and Java primarily, because they want to, for better or worse, do more generics - I don’t think it hurts to bring them in. I don’t think they’re gonna get everything they want either. But I think they’ll get enough of what they need to be able to consider Go for future projects… And that helps everybody.
And I’m not worried about somebody abusing it, because we’ve seen a decade of people abusing channels, and we’re still here. [laughter] So we’re gonna survive. And I think the community is better suited to be able to share with people, “No, no, no. Use runtime polymorphism here. Don’t –” We weren’t better suited to say that about channels, because we were all so brand new… I think we’re better suited to be able to kindly be able to fix thing in code reviews, where we realize generics shouldn’t have been used.
Alright, that’s a fair take. That’s a measured take, so thank you for that. [laughs]
Alright, it is time for Unpopular Opinions, y’all.
Alright, Bill, I know you came prepared for this. Hit us.
So this week somebody named Emily Freeman, who I do not know, but I just started following her on Twitter because she was just kind of saying interesting things… And I love that. I love Jaana, because she loves to say these interesting things… Even if I don’t agree with it. But I actually agreed with what Emily was saying, and I retweeted it, and what followed after was mind-blowing to me.
If you go on my Twitter account and find that, you can see all the comments that end up – it was mind-blowing. But this is what she said… And it seems that this is an unpopular opinion. She wrote “You know, when people hire artists, they look at their portfolio. They don’t hand them a canvas, some watercolors, and make them paint in front of an audience.” And obviously, she’s talking about how broken the interview process is… And I really believe that the interview process is broken, and I’m not a fan of these projects that you necessarily have to take home.
My opinion is this - if you have a strong portfolio on GitHub that you can share, and you’re confident that that body of work represents who you are as a developer, I think it’s fair for the other company to take the time to look at that, and then interview you against that body of work. The biggest arguments that I was hearing were people who were like “Well, I don’t have any open source, because I work all day. I work on private source”, and that’s fair. You’ve gotta find a way of being able to validate competency in anything we do.
And then I think people should have the choice. “Do you wanna take something home? It’s gonna take you N number of hours. Do you wanna do something live?” I think people should have a choice to do what best represents them. Because not everybody can stand up in front of a whiteboard – you don’t do that every day anyway… I don’t know. So it was interesting, and the feedback that I got through her was really mind-blowing.
I know Kris is gonna have some opinions on this… Before I yield the floor, I do wanna say that there is an argument that can be made on both sides. And over the years, having been subjected to all those different kinds of interviewing styles, I can see the merit for some, and in some cases I’ve hated those interview processes. But I can understand why people think they work, and in some cases they might… Because the thing is it’s not – I think it’s less the interview process, or I’ve come to learn that it’s less about the interview process, which may or may not be broken; it’s more about “Is it a good fit for the candidate that you’re interviewing?”
[01:05:54.21] In places where I have control over the engineering process, or rather the engineering hiring process, I try to have at least two different styles of interviews. One where I make it an option for somebody to do a take-home; if they have the time and the inclination, they can do that. And then we’ll bring you in and we actually have a discussion and you walk me through your thinking, your design philosophy as it were. That gives me an opportunity for us to have a back and forth, as if we were pairing on something together.
And then you have those who have been preparing, and they have the “Cracking the Coding Interview” book or whatever, and they know how to do the whiteboarding, they can recite algorithms off the top of their heads… Maybe they just graduated school and all that stuff is fresh for them, or maybe they spent the time and money and effort into preparing for these styles of interviews and they go to the whiteboard and they ace it; perhaps that’s suitable for them.
So I don’t think there’s a right way or a wrong way per se. I think there is what is suitable for different people, and I think being able to offer - and not everybody can do this. If you’re a small startup and you don’t have the people and the resources and money to do that, maybe that’s not right for you. But having different options, that suit different styles of interviewees/candidates, I think for me it might be the better approach, as opposed to having one way and basically trying to get signal from all the noise from a candidate for whom that’s not well-suited. You may be doing a disservice to your hiring practice… But that’s how I see it. Kris?
Let’s see… So I think that – I have a few minds on this. First, I think that our poor interview processes show how much companies in general don’t really understand what software engineering is. I feel like asking someone to sit down and write out some code – definitely when they ask you to sit down and write code in front of them, I think that’s absolutely atrocious. But I think even take-homes are a lot more noise than they are signal. Okay, you’re going home, you’re writing code by yourself, you’re not collaborating with people - that’s not what we really do at work. And I feel like a lot of even the better interview processes I’ve been through show that the company hasn’t really thought about what the people they’re hiring do, and what they would like them to do, and they don’t have an interest in figuring that out.
I feel like that’s the core problem with this, not so much that it’s like “We want you to show that you know what to do”, but that “We don’t really know what we want you to do, so we’re just gonna throw some random stuff at you, and we hope that it winds up being a good fit at the end of the day.” And I feel like that actually leads directly into the problems we see with bias and a lack of inclusion, is because we have these processes that boil down to “Oh, did you do all the stuff to jump through the hoops so you’re part of the cool club, because we don’t really understand how to do it better, and that sounds like a really tough thing to do, because we’re gonna have to look at our engineering process and sit down and figure out what it is we do, and that’s an even bigger and scarier problem for us to solve.”
So I feel like the interview process is kind of like the thing that we can see that you can grapple with the easiest, but I think it shows the deep, deep problems and struggles that we have as an industry. And I don’t think that we can actually solve it by trying to solve it head-on. I don’t think that there’s a way that we can tomorrow come up with an interview process that is good, and that will actually measure people’s ability to perform as a software engineer… Well, except for maybe like the kind of junior/intermediate positions of software engineering.
I definitely think we can make it better though… As I said, I think that asking someone to sit in front of you and write code is not only just really silly, it’s also extremely impractical, because that’s how you get people that get write hackathon code; that’s how you get people that don’t think through design philosophy. They’re just gonna bang out some code because they’re under pressure and they’ve got 20 minutes to show you that they know this programming language.
[01:10:01.22] And I think too they don’t actually tell you if someone knows a programming language, if that’s what you’re interviewing for. I can tell if someone knows Go by talking to them, because I’ve written enough Go that I know what to ask, so that I can see “Oh, you’ve been writing Go for approximately this amount of time, because of this way that you’re thinking about this type of challenge.” But we’re so focused on like “Well, we need to have some objective way of showing that they know the language” that we forget that there’s these far better, if more subjective ways of evaluating if someone actually knows the things.
I think that’s kind of my reaction to it… I absolutely agree, the way that we do interviews is terrible, and we need to fix it, but that is a very big ask for an organization to undertake, because it involves so many more things than just the interview process.
But to one of your points, I seriously do not like hackathons. I was asked to judge one one time and I said no. I said “You don’t want me judging this”, because I don’t wanna teach people that it’s okay to go into a room for three days and just program, and then get a reward for that. Make it clear then that you’re building prototypes. But let’s not pretend that this is a form of production-level engineering that we’re doing.
I think there’s just too many people coming out of these hackathons thinking this is engineering. No, it’s prototyping under extreme time limits… Which is great, but is that the engineer that you want on staff after that?
And then to Johnny’s point - yes, I think there are companies and teams missing out on people each and every day. But some of the stories I hear, like the questions that were asked, and [unintelligible 01:11:46.10] I’m like “Uhm, I wasn’t gonna pass that interview. I wasn’t gonna pass it.” And I feel like from a Go perspective I’m pretty knowledgeable, and yet I sometimes think “Whoa. I wonder what would have happened if it was me.” So I don’t know.
I’ve definitely failed some Go interviews, just because I froze up when I was going through the process… And I’m like “I don’t remember how to do this one thing right now. My brain’s kind of locked up, and now it looks like I don’t know Go, but I clearly do know Go.” I think that’s really problematic.
I will also add that I think that one of the things we need to start figuring out as an industry is how to better break up what software engineering is, because I think that there is this divide between the more designy engineering parts of things, and the more writing code and programming things, and I think they are different from each other, not one above the other. I feel like the structure of our organizations puts the software engineering things above the programming things, and as you kind of move up the ladder, you go from programming into more software engineering things… Like software engineering one, and two, and senior levels are all about the more tangible, lower-level, writing code, producing code, making that code good…
And then we have staff and above that’s more about this systems, and how all the pieces of software fit together, but those are two very different skillsets. And I think we really need to start talking about how do we separate those two skillsets and actually create not only interview processes, but ways for people to learn those different disciplines… Because I feel like so much of the time we get caught up in confusing the two of them, and when we don’t separate that, we lose a lot of the precision that we can get from focusing on one or the other. And I think this is what makes it really hard to hire senior-level or staff-level software engineers, is because you can’t give them algorithms; that’s not what they do. You can’t be like “Here, write some code for me.” It’s like, that’s not what they’re there to do. That’s not what they’re trying to accomplish. And I think more and more we need staff, senior staff, principal-level software engineers to tackle the problems that we’re facing, because we just can’t do it any other way. And if we have no way of hiring for them, then that’s a really big problem for us as an industry.
[01:14:09.06] What I wanna do, Kris, when my time with Ardan is up, I would love to be hired as a coach, and every week work with a different team at some company, to improve the design philosophies and the engineering and what they’re doing. Not sit down and write the code, but to be pairing with them and teaching them.
I think this role is incredibly critical moving forward, but there’s a problem with this role. At the end of the year, when your performance review comes up, you’ve got nothing to show for it.
You have nothing, no commits. I had a conversation with a handful of people. And the few times I’ve heard somebody have this role, it’s tended to end in a year, because during their performance review, it looked like they added no value, when they probably added ridiculous value.
So there’s a whole another show you should have here about performance reviews, and how consuming they are sometimes around January through March… And trying to intermix things that you’re doing; they’re maybe not even part of your job, but you have to, because you know what’s coming at the end of that year, and you have to show some metrics. That’s a whole another story… But there’s roles that are missing in engineering, because there’s no way to collect data on them.
Yeah, I feel like you’re absolutely right there, but I feel like even more importantly is that those roles add way more value to the organization than even one of those “10x engineers” that really just make a giant mess can add for your organization. I think that that is a chronically problematic thing that we have, where we just can’t evaluate the people that amplify what other people can do… Because at the end of the day, if you have ten engineers and you’re gonna go hire another one, then – would you rather hire one engineer, and you now have 1x more, or would you rather hire someone that can increase the productivity of your other engineers by some amount, through many different means? And if you can do that, then you get more engineering power with having fewer people. And there’s also all of the stuff around – which is probably part of this episode… It’s like the chronic thing where people are just like “We’re just gonna hire 40 engineers and not think about how to manage them, or grow their careers, or anything…” Yeah, there’s definitely a lot to talk about around this.
But you get code quality, you get your consistency, you get everybody on the same page with design philosophy, which is what we’re talking today, and you don’t try to do it in a week. You do it over the course of 3-4 months, right? And that’s the point.
I have an unpopular opinion that I will drop and run away from really quickly… [laughter]
We’re probably gonna have to talk about this a little bit, but… I feel that calling Go “Golang” is a respect problem and causes major inclusion problems within our community.
Wait, wait. I need more on that.
Okay. So here’s the line of thinking behind this. The language’s name is clearly Go. It is Go. That is what the authors have called it, that is what all the documentation says… And calling it Golang shows that, as a member of this community, you don’t have the level of respect to actually call something by the name it is intended to be called by… Which I think at a level – and maybe this is trying to draw a line too far… But I think if you can’t call something that you’re voluntarily being part of by the right name, how can we actually expect you to call other things by their right name?
[01:18:00.08] That’s where I get to the inclusion problem of like “If you can’t call a programming language by the right name, how can we expect you to call someone by the correct pronouns? How can we expect you to call someone who transitions and is a prominent member of the community by their correct name, and not dead-name them. And that causes lots of problems with inclusion at the end of the day… And I think that it seems small, it seems tiny, but if you can’t get your head around that, how can we really trust people to get their head around these bigger things?
Let me ask one question to you, because I think the problem here is though the language team early on made it clear that the language’s name is Go and not Golang - they made it clear - the problem is too many people, and we’ve got a million people here coming to the language, their first interaction with it is the search term “golang”. It’s like their first interaction with it. So from their perspective, “I use Golang on Google and all the stuff comes up.” So they kind of just transfer the name along. I think it’s done innocently. I think it’s done out of ignorance, because if you just type “go”, you may not necessarily see the same results as “golang”. It became the search term.
What I would like us as a community to figure out is how do you kindly, with the largest amount of kindness possible, tell somebody “Yeah, I understand why you’re saying Golang, but if you’re gonna do anything official, the name of the programming language is Go.” I wouldn’t tell anybody that unless they were writing something or they were putting slide decks together. I think there you can do some correction. But I think there’s just this grey area here where that’s their first interaction with it.
I agree, partially, I think… I’m not saying that it’s disrespectful for malintent, I’m more saying that it’s like “Okay, you thought it was Golang because you googled Golang to find something, someone has now corrected you… Commit it to memory that that is what you should call it.”
It’s the same thing with people’s names, right? I pronounce people’s names wrong multiple times, and it’s like “Okay, you pronounced it wrong, you got corrected, please pronounce it right.” Or spelling people’s names.
But I also think that leads to – at the end of the day, if we’re writing software, we’re writing really complex things that have a lot of nuance and a lot of precision, as we’ve been talking about. If you can’t separate out “This is what I use to google it, but it’s not its real name, but it makes it easier to google it” from “This is what the thing’s name is”, I think that’s a problem for us as a community when it comes to precision as well.
I think this is something that has those tentacles that keep expanding out the more that we discuss it… And as you said, it is something that we have to figure out as a community, how we want to solve it… Because I have run into some very flagrant and stark opposition to calling it Go from people that are like “Well, the website is Golang, and I write Golang when I google for it, so its name is Golang. I don’t care what you say about its name.” I think that’s the extreme end for sure, but it all trickles down, and we’ve gotta be real careful about where we allow that to be a component.
Simply put, I think most people feel like “Hey, calling Go Golang is disrespectful. Please call it Go” is a pretty basic place to start. But the reason I think it’s an unpopular opinion is because I think a lot of people are going to have hot takes and say “No, I think you’re wrong. We should be able to call it whatever we want.”
And that is that. [laughter] Thank you so much, everybody, for listening to this show, and contributing on Slack and Twitter and things. I do wanna have one last sort of parting set of words, going back to the whole interview thing… It is something that always generates a ton of discussion; some for, and some against, with regards to the different styles of managing it and whatnot. Rather than trying to solve that on the show, my message really for those who are subjected to these interview processes - if you go through an interview and they use a particular method that is ill-suited to your way of reasoning and thinking and communicating, it’s not an indication that you are incapable; it’s not an indication that you can’t engineer things, that you’re a failure, or that you don’t know what you’re doing, that you are indeed an impostor. It simply means that the method that they’re using to calibrate your skills is not well-suited to you and how you learn and how you communicate. So don’t beat yourself up too much; just move on to the next. And if you don’t get your dream job, that’s okay. Maybe it’ll come back around in a couple of years. Keep your head up, and – you know, interviewing is hard, for both interviewers and interviewees; it’s a challenge… But it’s not a reflection on you and what you’re capable of.
With that, I’ll say thank you for your time and listening, and we’ll catch you in the next Go Time.
I want this to be unpopular. I keep failing with my unpopular opinions. They keep being popular. I’m like “No, it’s not supposed to be popular. Stop voting for it.”
It was a good episode of Golang Time, guys…
There wasn’t much chatter on Slack, so I don’t know what that means… I guess that wasn’t there to cause disruption…
It’s weird, some days it’s really lots of chatter, and some days it’s not. There’s no rhyme or reason.
It’s just the ebb and the flow of things sometimes…
Yup. I was listening along and I was enjoying it a lot.
That hour went fast.
I’m glad I wasn’t on it. I’m glad I said no. Because y’all have lots of smart things to say and I had nothing smart to add, so… [laughter]
Our transcripts are open source on GitHub. Improvements are welcome. 💚