Episode 82

ORMs vs SQL

00:00:00
/
00:44:37
Your Host

About this Episode

The panel digs into the perennial question: how much SQL should developers know? Kicking off with a war story, Mike recounts a hyper-growth phase where ~20 performance issues were fixed—almost all by database changes, especially adding (or rethinking) indexes—yielding order-of-magnitude speedups. The moral: ORMs are great for safety and productivity, but when things get slow, it’s “usually the database,” and knowing how indexes, JOINs, and query patterns work is what unblocks teams. Will adds a blunt rule of thumb: apps are slow because of “bytes on the wire” or “the database,” and you can’t rely on ORMs alone to prevent N+1s or inefficient access patterns.

From Ops, Kyle reinforces that troubleshooting still lands on SQL: monitoring tools can point to hot spots, but root-causing and backups often require direct database savvy. Eddy shares a counterexample: moving search from Elasticsearch to Postgres full-text revealed that a GIN index on a high-churn table actually slowed writes—illustrating the trade-off that heavier indexing speeds reads but taxes inserts/updates. The group also debates concurrency: for most web apps, you can push work “down the stack” to the database and avoid complex threading; true low-latency, hard real-time concurrency is rarer than many think.

Stepping back, the crew frames SQL as a declarative, optimization-friendly paradigm—closer to functional transforms than procedural loops—which is precisely why database engines can do so much heavy lifting. Resources like SICP and Lisp/Scheme are recommended for learning to “think in streams” and transformations. The consensus: programming languages and frameworks come and go, but SQL endures—and senior engineers still write ad-hoc queries daily to answer business questions and debug production. They close with a teaser for next time: a lively debate about testing private methods and how to design for testability without committing “crimes” against your runtime.

Transcript:

DAVID: Hello, and welcome to the Acima Development Podcast. I'm David Brady, your host today. And we've got a fun panel today. We've got Kyle; we've got Eddy; we've got Mike; we've got Will, and we've got Matt Hardy. Welcome, Matt. Nice to see some new people, well, not new, recurring people. We don't have anybody new, new, new, but nice to see the regulars.

Today we're going to talk a little bit about SQL. Like, do developers need to know it, and if so, how much? And this came as a suggestion from Kyle, who works more in ops rather than frontline web dev. And so, I'm very interested in hearing where he wants to go with this. But first, as our tradition, let's start with some story time with Uncle Mike. Mike, what do you know about SQL?

MIKE: [laughs] So, I do have a story, and I was prepared for this. A few years ago, I say a few, at least five years ago, maybe five or six years ago, I was in a big application that was growing up [chuckles]. We were actually getting a lot of traffic where we hadn't been. It's the startup journey, right? And this actually was at Acima, you know, as we were in our rapid growth era, not that we're not still growing, but, you know, back in the startup days.

And it's natural for every application, once you start going through that growth period, like, oh, wait, there's some things that don't work very well, let me say, that don't perform very well, that don't perform very well. There's things that work just fine when you had a data set of a thousand that don't work very well when you've got a data set of a million [laughs]. Things just don't work quite the same anymore.

And so, I went through with the team, and we worked on that for maybe a couple of months. And over that couple of months, we made the application run about 10 times faster, which is a big deal, right [laughs]? That's a major performance impact. I think it was more than 10 times. I mean, it was a big deal. You know, going to 10 times faster has a big effect on your infrastructure, and I think it even went even beyond that.

But I kind of quit keeping track after a while [chuckles] because it's, you know, you start resetting your baseline. Okay, well, we got to go faster than that, got to go faster than that. It’s not the only time I've done this, right? It's just the most recent time that I have fresh in mind.

And one thing we found is...let me say a couple of things. Every single one of the performance improvements we made, except for one, were database improvements, and most of them were adding missing indexes. So, there's a database table missing an index, and, by the way, it's always a missing index. If you have a performance problem, it's a missing index [laughs]; I'm just going to say that, except there was a couple of them that weren't. A couple of them were some kind of weird cases.

There was some really gnarly JOINs. It was running, like, JOIN against 10 things using a lot of LIKE queries. We fixed that one by using an external index, using a reverse, one of those external reverse indexes. It was Elasticsearch at the time. And that made a tremendous difference as well in our database load.

And there was one that was not a database issue, you know, so we fixed, like, 20 issues, one of them wasn't. And it was because there was something that should have been in the database [laughs] but wasn't. There were some permissions that were being loaded really strangely from flat files and weren't being cached, and every request was reprocessing them. And by putting a little bit of caching and changing the processing, we took that from taking, like, half of every request time down to unmeasurable, right? It just went essentially to zero. It was great, you know, app went way faster. Everybody's happy. Everything's wonderful [laughs]. And we work on other problems.

The lesson I learned from this, though, is that, yeah, it's always the database. And here's the critical thing: adding a database index is something that's really, really easy to miss if you don't know something about how databases work. And most of the time, you don't have to think about it at all, except for that time that you do, and it's the thing that matters most. I found that throughout my career, that yeah, it's always a database index [laughs] or something to do with the database because you don't have to think about it except for that time that you do. And if you don't know that time, then you're burned. And somebody who can come in and can fix that can make a tremendous difference in your application.

Oh, there's the story that I was thinking about as we talked about this topic.

DAVID: Awesome.

MIKE: Do you have to know SQL? Well, I come from an older generation, right, where I cut my teeth writing raw SQL. And I thought it was kind of amazing when we started getting ORMs that would do some of that automatically. There's a whole lot of advantages to that. So, you avoid a lot of SQL injection flaws. You probably get better queries most of the time. So, overall, the switch that most applications have made to using an Object-Relational Mapper (ORM) is a great one. But that experience that we got when we had to do it manually is still incredibly useful. And I worry a little bit that the new devs coming in now are not getting that experience that you usually don't need, except for that time that you do.

WILL: There's only two reasons that your app is slow: bytes on the wire, or your database sucks. That's it.

MIKE: Right [chuckles].

WILL: And if your API is bad, it's just because their database sucks. That's how it works, you know. And, I don't know, I love an ORM. ORMs are great. ORMs are great, and I use them all the time. And, like, 99% of the time, it does it every time. But that's why we make our money. That's why AI is not going to come and get me this year. It's because sometimes...if the junior devs could do it, they would have done it, and it wouldn't be on my desk. And, I don't know, like, you've got to run some gnarly SQL.

I'd also say two things: one, it's not like regular programming. It doesn't follow the same rules. It doesn't work the same way. It doesn't work the same way. SQL, if you're writing a for loop in SQL, you’re f***** up. My one F bomb for the podcast. Let me take it early.

It doesn't work like that, right? And it's not that it's inefficient, but it's just it takes a foundationally different logic to it, and it's everywhere. My PM he was having terrible troubles in Jira this week because our team and all of our Jira stories were getting communicated up to management that was saying, like, “Thumbs up, thumbs down,” Roman emperor style.

And this Jira query was screwed up, and I'm like, I don't know JQL, but I know JQL, and let's face it. And I just got in there, and I fixed it. And it's like, it's everywhere. It's everywhere. It's a foundational programming paradigm that is in everything. You want to do some GraphQL? You should know SQL. It goes in everything. There’s all these sort of, like, abstract layer data management things, and they're all heavily derived from SQL because they're solving the same kinds of problems.

MATT: You should know what's under the hood if you're working on the car. Simple as that. And it's really easy, with an ORM, to get in trouble and write a bunch of N+1s if you don't understand what's going on and what's happening under the hood.

WILL: Absolutely. Absolutely. Like, if somebody asked me and they came in and they were like, “Hey, listen, I have time to learn one thing this year. I could do concurrency, or I could do SQL.” I'd be like, “Just save yourself some time and learn SQL.”

I believe, sincerely, that we're pretty close to the level in programming where most developers will never need to know anything about a thread, not really, you know? You've got an async/await. You can run some coroutines, just kind of spawn a job queue, which you can do, not knowing anything about real concurrency. And you can live and die a productive life without having to wrestle that pig. SQL is not that way, though.

MIKE: Agreed. I may have told it before, but the gnarliest problem I ever worked on as a professional developer was a concurrency problem, and it probably shouldn't have been [laughs]. You get yourselves in all kinds of trouble there. And suffice it to say that there was multiple bugs in the library I was using [laughs], and I had to fix them. And solving a problem that is both concurrent and distributed is hard [laughs], especially with problems in the library.

MATT: Yeah, and few applications require true concurrency.

MIKE: Exactly. If that concurrency had never been in there, it would have worked better [laughs]. And it was the wrong solution for the problem. But I use SQL, well, all the time. This morning, somebody said, “How do I get this data?” and I wrote a query for them.

MATT: I mean, here at the company, we have hundreds of services, and I think one or two actually require real concurrency.

WILL: Yeah, I mean, I'd be surprised, like, what services do you have that require real concurrency, like, a web app? Like --

MATT: Underwriting-related things.

WILL: What's that?

MATT: Underwriting-related things.

MIKE: Lots of parallel requests going out to third parties.

WILL: Still skeptical, but I don't know enough about it to have, like, a strong opinion on it. I mean, because, I mean, I'll tell you, like, honestly, like, really how, like, how things work for really real is, like, all that concurrency, like, modern web apps are concurrent as hell, super concurrent. But you know who does it all? SQL [laughs]. Like, oh man, I got all these parallel requests that I need for, like, high-performance web apps. I’m like, oh man, you don’t need all that. Just kick it down to the database. Let them sort it out.

MATT: Yeah, I mean, it's necessary when you need responses from 12 different third parties at the same time, right?

WILL: Nah, [inaudible 11:19] can wait. You just wait them all. Wait on all of them. You don’t need a semaphore for that. That’s like...when I say, like --

MATT: I would argue, Will, that SLAs say differently.

WILL: Naah. No, no way. Not a chance. Not a chance. Like, your performance isn't that good. Like, if I’m going to network stack, my performance...I don't want to say it like thaaat. But, like, so I mean, for background, right, when I'm talking about, like, real serious concurrency, like, what I refer to specifically in my work, right, is that you work on the radio stack, not TCP, not IP, like, bits and bytes on the wire but wired less, right? Like, working the radio.

I also do a lot of, like, real-time audio programming, and that stuff is serious concurrency. And there's other stuff. It’s like async/await. It's okay. I'm going to fire it off on async, and I'm going to wait for it to come back. We'll, you know, join all my promises together, so I have this big chunk. But, I mean, like, milliseconds, like, a millisecond is an eternity, you know, and, like, you know, on the network, a millisecond is almost not even measurable, you know. Anyway, maybe there's something exotic, and if you're not doing, like, high-frequency trading, like, I don't know, probably the technical detail’s not something that's appropriate to get in here anyway.

DAVID: There's a CS professor at the University of Utah, can't remember his name, but the best quote...he was an audio CS guy in the ‘90s back when, you know, a 486 was all you had and, you know, at 33 megahertz grinding away.

WILL: [inaudible 13:06]

DAVID: And a friend of mine was in his audio processing class, and efficiency was the buzzword. And he finally says, “So, I have to do this before the next stroke of, like, sending bits to the sound card.” And the professor goes...I'm sorry, the professor wasn't in audio. He did some audio, and he didn't realize he was talking with my friend about audio. And he looks at my friend, and he goes, “Wait, you're doing audio? You'll have milliseconds,” and walked away. Literally, that is a lot of how concurrency is today, right? It's like, I got a four gigahertz processor. I can chew this elephant from front to tail serially and just take a blocking operation, and it's two, you know, two milliseconds or whatever, and you get through it.

I've had one app where concurrency was critical, and it's anywhere you need to be handling multiple threads of attention live or giving the appearance of live. I wrote an IRC client, so you have to catch keystrokes coming in from the user before they hit enter, so you can't do an input read statement. You have to stroke, stroke, stroke, stroke, and update that. Meanwhile, the server is throwing network bytes on demand at you over the fence, and you can't re-request them, or it's like, you have to handle those network packets when they come in. And that was the time when we had to interleave.

MATT: You just gave me flashbacks.

WILL: Yeah, the battle days.

MATT: I've written a few IRC clients back in the day.

DAVID: Getting off topic, but IRC is what broke me of Python. I was in love with Python. I'd come from Perl, which is super cryptic, and messy, and dense, came into Python, which was clean and beautiful. And you cannot condense it down to a single line. And when you are scripting an IRC client, you have one line to do everything, and that's what pushed me...It pushed me back to Perl and then forward to Ruby.

WILL: Interesting. I don't know, here there be dragons. Don't mess with concurrency. Don’t do that.

MIKE: Exactly. That's the moral of the story.

DAVID: Touching back, though, a little bit, there are times when you have a high-level framework, and it starts acting weird, or you change a feature, and something completely unrelated changes. And if you are very familiar with, like, SQL and how database engines work, that's when you go, oh, we're reading the leases table on a new column. I wonder if it's not indexed because that would slow everything down in this chain, right?

And it's, yes, we do...I'm old enough that I was in the generation of people that had the math teacher that said, “You won't always have a calculator with you. You need to learn your times tables.” Well, I have an iPhone. Well, I have an Android. I've got a computer in my pocket now. You were wrong about that, Mr. Nelson.

WILL: No, he wasn’t.

DAVID: SQL might be headed that way, but right now, you don't always want to pull out your phone to be a calculator. And when you can just look at a website and go, that's misbehaving in this specific way, and I can see through the ORM to the underlying technology, it’s super powerful.

MATT: Well, it provides value.

WILL: Well, Matt brought this up, and I think that nailed it. I mean, in that, like, it's really easy if you're in an ORM to write N+1 queries, right? To write a double-nested loop instead of, like, just getting the whole enchilada.

And I think I would liken it to memory management in that, like, nobody, I mean, much like, you know, much like concurrency, like, nobody's malloc-ing anything if they have any sense in the year of our Lord 2025. But you still need to know how it works because you can definitely leak.

MIKE: It's not that hard to do. Just make a global hash in memory, and keep it persistent.

WILL: Yep. Yeah, man, I have a ticket in my backlog right now, which is, like, go leak hunting. You’ve got to dump that heap. Kyle and I we've dumped a heap or two, you and I [laughs].

DAVID: Actually, that reminds me of something else. We said at the top of the call, Kyle, you live over in Ops land. What are you doing talking to databases?

KYLE: What am I doing? A lot of it has to do with we manage our RDS instances, and it's a lot of the time troubleshooting or backups. Those are the bigger issues. That's kind of where I thought about this topic is because in prior organizations, I mean, older codebases, older companies, they didn't use ORMs. A lot of it was just straight SQL. And so, everybody that I spoke to they knew and understood SQL.

And one drawback that I have seen with ORMs, while they are great, is when you need to get in and troubleshoot and dig around, you need to find the engineers on your team that actually know SQL, because not all of them actually know SQL, in order to help troubleshoot with you, right? There are tools out there that kind of bridge some of the gaps and will identify, oh, you don't have an index or something on your table, which we utilize. We've got New Relics and Data Dogs of the world that kind of help us identify some of those things. It gives us benefits in some areas.

In other areas...I think somebody said, 99% of the time, it's great. And then you run into the 1%, and then you run into an engineer that is not very versed in SQL. Not to say that I'm great with it either, but it does slow down troubleshooting. It does slow down figuring out the root cause of the database problem. Because, like Will pointed out, if your app is slow, the first thing I'm going to is the database. Is something happening with the database, right? Because that's where most of the problems come from. If it’s not the database, it's something like Redis, another pseudo database, or --

MIKE: Another database.

KYLE: Another cache location, right? It's always in these locations. And ORMs make it really easy to develop. They make it a little bit more difficult when you need to troubleshoot. And I won't even say just ORMs, ODMs, too, anything that's doing it for you or helping you without actually teaching you the underlying layer. But, yeah, that's kind of where it is for me.

And even some of the toolings for doing backups, when it comes down to it, we've chosen to do backups with straight SQL. Because this is speaking outside of the ORM, but some of the tooling we have that we've tried it's not as fast, and we've had to optimize with straight SQL commands.

WILL: Well, I mean, I say it's a clutch move that I've run into many, many, many times. It's like, when you push the logic down a layer, if it's things that the database thinks about very efficiently, right, you can push things down to the database because the database has all of these internal optimizations and tools, and it doesn't have to go back and forth. You could skip steps.

And if you can have, I mean, obviously, there's caveats there, but if you can have the thinking happen on the database, you're looking at 10x improvements because databases are optimized to an incredible degree, internally, internally for the things that they think about, right? You would never put business logic in there, but in terms of, like, munging data, it's so much better than, God help you, Ruby [laughs].

MIKE: Well, you say 10x. 10x, that is super pessimistic. Because just making that network call, the moment you make that network call, and we’ve talked about this before, it dominates every other thing by far in that request. Because making that network call, you're going from milliseconds or microseconds to, like, seconds sometimes, right? You can't define that.

Now, it's probably milliseconds, right? But it might be 30 milliseconds or 50 milliseconds, whereas if you're running that thing in memory, we're talking less than a millisecond significantly. And it may be your database. Maybe it's a really hairy, gnarly query, and maybe it'll take two milliseconds in your database. That is still wildly faster than bringing it back into memory on your side, doing everything, and then making a request for every one of those pieces, creating a new network request back to the database. If you are only getting a 10x improvement, you might have done something wrong.

KYLE: I worked with an engineer a while back on card processing. And they needed to do the card processing in a specific time window. And it was one of those things where they really wanted to blame the database, and they really wanted to narrow it down to that. And I got in there, and I looked, and we're sending, I can't remember at the time, but it was hundreds of thousands of requests to this database.

And I got looking, and the highest turnaround time for any one of those requests was about two milliseconds. And it's just like, you're already offloading a ton of work to this database. Scaling this database isn't going to do you any good. We're already executing this stuff faster than it's transferring across the wire. Like, we need to look elsewhere.

But, like, I bring that up in this context just to say, like, yes, databases do handle things really well. And also, like, in this case, an ORM was used, so, I mean, it was efficient. Like, there's definitely efficiencies there and good use cases for sure.

WILL: I mean, sometimes you can flog the database a little bit too much; believe me, I’ve done that play, you know? Can I get a bigger instance? No. Oh [laughs].

MATT: You don’t need to tell us, Will. Kyle and I just dealt with that, what, two days ago [laughter]?

KYLE: Maybe. Maybe.

WILL: Yeah. That's when things start getting really gnarly, when it's like, okay, can I just put a bigger engine in the car? Actually, no [laughs].

KYLE: Well, and it's interesting, too, because it's not even...usually, at least in the cases that I've seen, it's not that the database can't handle it, so the engine is big enough. It just doesn't have enough seats to handle all the connections. In most cases I've ever run into, it's, we're tipping over databases because we're just talking to it too much. We're sending way too many connections to it.

MATT: That was exactly what was happening.

KYLE: I didn't want to throw you under the bus [laughter], but since you spoke up [laughs]...

MATT: It’s okay. I mean, to be fair, I did not write that, but...

KYLE: That's fair.

MATT: But we did get it resolved.

WILL: The last guy out always takes the arrows. It's like, all right, who quit last? [inaudible 24:26]

MATT: Well, I am responsible for the service that was happening, too, so I will take that.

WILL: You will take responsibility for finding the guilty party and seeing that they're punished.

KYLE: Matt identified the dip. It's fine [laughs]. We know who did it [laughs].

DAVID: Fantastic.

MIKE: That's why it's called git blame.

DAVID: Yes.

KYLE: Blame, right?

DAVID: Subversion, the predecessor to Git, had SVN blame, and the author hated that it was such a loaded term, so he added an alias, he or she; I don't know who actually maintains that. They added a praise, and it's just an alias for blank, an SVN praise to see who did this. I don't know if they still have it. I think it got retired because nobody used it. Absolutely nobody is going to Subversion anymore.

MATT: [crosstalk 25:10] uses Subversion anymore.

DAVID: Mm-hmm. While Subversion was still extant, they were like, yeah, nobody is using this. We only ever go to the code in anger.

MATT: It's true. I don't think I ever used SVN praise. I did use SVN blame.

WILL: I don't know. I agree. I agree. We shouldn't have called it blame. But that was probably Linus' fault, and, like, he probably did it for a reason.

MATT: It’s snarky. It's a little funny.

DAVID: Good times. Eddy, I messaged you during the call here. The story that Mike gave us at the top had an interesting phrase in it, which is, it's always a missing index, except when it isn't. And we actually ran into that. I don't want to steal any of your thunder. Do you want to tell the story of the stuff that we worked on with Bill and why we had to do it?

EDDY: Sure. I'm not very inclined with exactly, like, I don't have all the context in what we were running into. I'm not a database guru. But the bare bones essentials is we have a project that's currently leveraging something called Elasticsearch, right? And, historically, you know, anytime we needed to make any changes to it, right, it's brittle in our context. You know, it's always been a little bit shaky, you know, and inconsistent, always caused a little bit of turmoil with experience.

So, we had some time to really research, you know, an alternative, you know, to replace something like that and just sort of bring it in-house. And so, we had some time to look into something because we use Postgres at Acima. And so, it turns out that Postgres actually handles text searching and matching natively, called Full Text Search.

And sort of the way that works under the hood, right, is that it uses a GIN index that gets added into a table, which then passes in a cascading TSVector into any joins table that it wants to match any strings on it. The problem with adding an index to that degree, especially for a table that we were adding it on, was that it had way too much activity on it. I think it was up to, like, it was for every 20,000 inserts, you had, like, 10 times updates [laughs]. So, like, the amount was basically tenfold with updates versus inserts.

And so, what we ended up doing is we ended up bringing a copy of production. I didn't see any of that data, I should say. It was piloted by another database.

DAVID: For DBA. For a DBA.

EDDY: It’s for a DBA. So, I had no access to that whatsoever. The bastion for that individual was only to that one person, no one else. I didn't see anything, so no PII. Anyways, the point is we were able to mimic, quote, unquote, “activity” to what we were to expect to happen in a production environment, but in a lower environment setting. And it turns out that by adding a GIN index with the nature of how that works, right, we actually ended up slowing down the number of how fast that table was able to process connections.

So, like, long story short, I mean, I guess, like, 9 times out of 10, you can say, oh yeah, index is always the way to go, except in our scenario. When we're adding a GIN index, it actually made it slower. So, really something to keep in mind where, like, you do have that really one edge case, right, but it’s not one tool at all, right? Like, you do have to be cognizant of what index you're leveraging, and it can have, like, cascading effects like what we saw, so..

DAVID: Absolutely. The general case of this is that the more heavily you index data, the slower your inserts and updates are going to be because every time you insert or update a record, you've got to go update the indexes, right? The whole point of an index is it's pre-calculated. So, when you modify the data, you've got to go redo the pre-calculation, and that totally makes sense.

I cut my teeth in databases, oh, man, [inaudible 29:25] to SQL base. But the one you might have heard of would be MySQL was where I started. And MySQL, like, 3, 10, 15 years ago, generally, had two types of tables that it liked. One was...and I can't remember what they were called, but one of them was basically fully indexed, and inserts were kind of slow, I mean, normal speed, I guess, because this was the normal type of table.

But it also had kind of like a logging table, and inserts into that table are very, very fast, but you cannot put indexes on the table. You can only read it sequentially. You can read it any way you want. It's just slow because it's not indexed. And so, if you've got, like, an event stream coming off of something, one of these tables is great because you're not ever going to query it in production. You're only going to query it, like, in the data warehouse. And when that happens, then, yeah, strip all the indexes off because that's going to speed up your inserts and your updates.

MIKE: One thing that Will mentioned a while ago is that SQL is very different from other programming. And I wanted to latch on to that a little bit because I think that there's some...I think it has a lot to do with what we're talking about. We don't use it because it's weird, right? And the way that it's weird, I think, is relevant.

So, SQL is declarative; it's not procedural. That is, it's not allowed to have side effects. You just declare the transformations on the data and what you get out, and then your compiler for your SQL, right, does everything. And that may sound familiar to anybody from functional programming, which tries to make the language like that as much as possible because situations where you can just be declarative and not procedural can be optimized like crazy.

And they actually represent what we're doing most of the time as engineers. We're taking data from one place. We're changing it around a little bit, and we're sending it to another place. I mean, that's our jobs, right?

DAVID: Mm-hmm.

MIKE: We grab the data from somewhere; we tweak it a little bit, and we send it somewhere else.

And if you have a language that will only do that, you can have your interpreter for your SQL that will create some really optimized code that will do that, and you don't write the procedure at all. You have no idea how it's working, and you don't have to because you're thinking about relational algebra, and [laughs] how does this work from a high level?

Much like you would do in a functional language, where you're just thinking about the output and the transformations, and you write your code as a series of transformations. You worry about types and about output, and you don't worry about how to do a for loop. And that's usually considered best practices in modern coding is to write your code in a functional style that writes those transformations because it avoids a whole class of bugs.

So yeah, it's weird, but it's good weird, right? Now, it's an old language. It's got some warts, right? It feels kind of old, maybe, but the principles it's built on are solid. You learn the syntax; it works great. And thinking that way is, I think, one of the most important things that an engineer, and I tell this to junior engineers all the time, it's one of the most important things that will help you in writing good code.

If you can think about your transformations as being a series of transformations, well, what's my start data; what's my end data? Don't worry about how it gets there, and then let the compiler or your interpreter do the job, right? And instead of you doing it yourself, you're less likely to have bugs, and it'll probably be faster. Yes, it's weird, but [chuckles] if you can start thinking that way, you're probably going to write your code better anyway.

MATT: Old weird, but good weird. One word, Erlang.

DAVID: Erlang, mm-hmm, getting on the beam. I'm scouring my bookcase from here, and I'm in the middle of moving my office, so I can't find the books that I'm looking for. I cannot recommend highly enough SICP, “Structure and Interpretation of Computer Programs.” This is a free class taught...you can download these online. There's a book for it. This is a CS class. The video quality is terrible because it was recorded on a VHS in, like, 1986. It is old, old, old as dirt. And if you are a web programmer, there is stuff in there that is rocket science world --

I went through the SICP lectures in 2009, and I was furious halfway through that none of the senior developers around me had a clue what was going on in this class. One of the biggest things that they will teach you in that is how to implement streaming, how to think in terms of streams. Instead of in loops or in long blocking things, it's just, what's the next thing, what's the next thing, what's the next thing?

And you have to understand streaming if you want to do work on a collection that could potentially be infinitely long or human input, where it's, like, every keystroke is just a new keystroke, and you can't iterate over every keystroke that's going to be entered into the program before they've been entered into the program. So, you literally just have to sit there and wait for each keystroke to come off.

Handling those streams, because it's from the ‘80s, the same math problems on the same whiteboard drop out when you go talk to the database theory people, as the SICP guys. When they start talking about streaming data out of the system, they're like, “You guys are writing a database engine. 100%, this is a database engine.” Coming back from SICP, the drawback to SICP is it will teach you how to do Lisp. The advantage to SICP is it will teach you how to do Lisp, and learning Lisp will make you a better programmer, I promise you that.

MATT: 100%.

DAVID: I don't know a single programmer who has learned Lisp who has not 100% agreed with that statement. It will make you a better programmer.

So yeah, Structure and Interpretation of Computer Programming. And if you want the light version, “The Little Schemer.” Scheme is a dialect of Lisp. You can get it on your computer right now. It's free, and you can learn to program in Scheme, which is, again, a smaller dialect of Lisp. And the first time you learn how to write your own multiplier, when the only tools you have in your pocket are add, subtract, and is this number equal to zero? You have no if tests other than is this zero, which is fantastic.

Write the equals equals for a pair of integers when all you can test for is zero. You're going to need a subtractor, and you're going to need a lot of time if the integers are large.

This has been a good bit of chatter. I think we're at a pretty good closing up point.

MIKE: You know, one thing I wanted to latch on to, again, and I think Will brought it up also, as to AI not taking my job tomorrow.

WILL: Not this year.

MIKE: And I alluded to it before. I have, you know, as my career has evolved over the last few years, I don't write very much code anymore, mostly directing things, right [laughs]? I'm moving things from here to there, you know, communicating. But I still write SQL regularly, close to daily. I write in queries because I need to get information out. And they're ad hoc, right? And they're going and pulling out data that other people don't know how to pull out because, you know, they're not sure where the data is, which says something to me. When everything else goes away, what's left is SQL [chuckles] because it endures forever. You know, your career starts with that, and it seems to end with that as well. It is just always needed.

And yeah, you might not need it for every piece that you're writing because the ORM probably should be used to do most things. But knowing that is probably, you know, the most valuable thing I've learned in my career in terms of programming. Other languages come and go; SQL endures.

MATT: I would agree with that. It's been the only thing that's always been there in my career. Tons of different languages, always SQL.

WILL: Like, when I'm cracking up a codebase, right, and I'm like, okay, what are we doing, right? Like, the two things I look for, when I could find them, especially, like, I go to the schema, like, that schema.rb. What's in there? Who was your daddy, and what does he do? And then I look for the API requests. If I've got the schema and the API requests, I know what's going on. And it's usually not that hard to read. You know, I mean, like, you'll have, you know, maybe 50 tables, you know, and, like, you know, 50 tables, and, I don't know, half as many API requests. And if I know what's going on there, like, I got you, you know, maybe for web apps at least, you know.

DAVID: 100%. I had the great privilege of hanging out quite a bit with Sandi Metz, and she's just an amazing programmer and coach. And she's really, really good at teaching people how to think, which is so much more rare than being able to think. She can teach thinking, which is incredible. She and I were chatting at a conference, and I was talking about, like, things that change over time. And we got to talking about databases and ORMs, and she said something really interesting. She said, “I've never seen a company evolve its database stack,” like, moving forward.

And I thought about it, and I'm like, have I seen...and what she said was, “Your tech stack will change. Your database will live forever.” One day you're going to wake up, and somebody's going to say, “Let's get rid of Ruby on Rails, and let's switch everything over to Groovy,” and it'll be a three-year project, and your tech stack goes away.

I have migrated databases under the same tech stack, but they have all been the same migration, which is migrating to Postgres away from a very expensive vendor lock-in, like SQL Server, or, you know, one of the big, big, big, big expensive ones. That's the only time I've ever changed database flavors mid-roll on a serious production system.

WILL: Yeah, I was going to say that is why I think many developers of our vintage have an almost pathological aversion to that one database vendor.

MATT: We all know what you're thinking [laughter].

WILL: And if you know the one, you know the one. And if you don’t know the one, it's because whoever raised you [laughs]...like, we don't say that word out loud, like, Voldemort [laughs] for developers that were like...

MATT: I have a friend who's very high up at that company.

WILL: And I think that [inaudible 40:37] a whole lot of money.

MIKE: And if only there was, like, a really wise person you could...I was going to say, if only there was, like, a really wise person who [inaudible 40:43] you could go talk to and ask this question to, that would be really helpful.

DAVID: Sandi is incredible. She is the first person that...Mike, you said something earlier. You said, always, always, always accept when you don't. And she and I were at loggerheads over, do you test private methods? And we went around for, like, 20 minutes. And then, by the end of it, she went, okay, yeah, never test private methods unless you want to, and then test them as much as you want. Because she had found the edge case, right, which, well, what if I really, really want to? And she’s like, if you really want to, go for it. Anyway, that's a side ramble. That's a P.S. for the podcast. I think we’re good.

WILL: Wait, so are you on team test private methods or team don't test private methods?

DAVID: I'm on team never write private methods, ever. So, testing...

WILL: What?

DAVID: If you've got a private method, you have logic that could break and should be tested. But you've put it deliberately at an impedance mismatch to the stuff that you're testing. And you probably have a better...you have a public class hiding inside that class that if you just name that thing, that becomes a private class. You pull it out. You make it a public class. Those methods will become public. And you guard access to that privacy by making that class private in the class that wants to use it. You're making your poop face. I love it.

MATT: Sounds like we have the topic for our next podcast.

Mike: [laughs]

DAVID: Oh, we could do a battle royale on testing private methods or not, absolutely. Because there's a... I've been very dissatisfied with all three positions. So, I don't actually have a, what do you call it, I don't have a sacred cow in that hamburger factory.

MIKE: I've got a strong stance on testing in that it doesn't matter whether you have private methods or not. Because if you write your tests right, you're just following every branch of logic, of input logic. What are all of the different types of input that you have that could be branched on in the code?

WILL: I've existed in ecosystems where testing on a public-only policy requires some crimes against God and man in terms of manipulation of mocks, and run time, and stuff like that. The big, ugly one is React, where there isn't a direct run loop input-output. If you're dealing with sort of like, hey, if I do this, what's the UI going to look like? Am I going to see all these elements the way I want them to? Which is obviously a test that you would have an interest in writing. But you have to commit crimes against God and man to sort of force that run time and then loop to arrive at a sync or a fixed point or whatever. So --

DAVID: Cool. Will, let me put a pin in that for our next episode, because we could go another hour real easy because I'm ready for this. This is awesome, so yeah.

MIKE: So, we've got the teaser for the next episode.

DAVID: We’ve got the teaser for the next episode. Fantastic.

MATT: And it won't get heated [laughter].

WILL: Horrors of testing.

[laughter]

DAVID: It won't get what?.

MATT: I said it will not get heated.

DAVID: Get heated. No. No, we're all friends here, so...

MATT: It’s true.

DAVID: Fantastic. Yep. Thank you for listening to the Acima Development Podcast. We have been Acima Development. And, Will, you’re honorary. You're an alumnus, so...

WILL: And also Will [laughs].

DAVID: And also Will. There we go. People we love and also Will and...so, thank you for listening.

WILL: [laughs]. Acima [inaudible 44:16]

DAVID: Yes, I like it.