The Changelog: Software Development, Open Source - JavaScript sprinkles in Basecamp turned Stimulus (Interview)
Episode Date: March 6, 2018David Heinemeier Hansson joined the show to share the story of how JavaScript sprinkles in Basecamp evolved into a full-fledged framework called Stimulus. We talked about ins and outs of Basecamp as i...t is today, Ruby, JavaScript and David's somewhat new found love for that language. How they open source because they can. And David's new YouTube series called "On Writing Software Well".
Transcript
Discussion (0)
Bandwidth for ChangeLog is provided by Fastly.
Learn more at Fastly.com.
Error monitoring is provided by Rollbar.
Check them out at Rollbar.com.
And we're hosted on Linode servers.
Head to Linode.com slash ChangeLog.
This episode is brought to you by Rollbar.
Rollbar is real-time error monitoring, alerting, and analytics that helps you resolve production errors in minutes.
And I talked with Paul Bigger, the founder of CircleCI, a trusted customer of Rollbar,
and he says they don't deploy a service without installing Rollbar first.
It's that crucial to them.
We operate at serious scale.
And literally the first thing we do when we create a new service is we install Rollbar in it.
We need to have that visibility.
And without that visibility,
it would be impossible to run at the scale we do.
And certainly with the number of people that we have.
We're a relatively small team operating a major service.
And without the visibility that Rollbar gives us
into our exceptions, it just wouldn't be possible.
All right, to start deploying with confidence,
just like Paul and the team in CircleCI,
head to rollbar.com slash changelog., head to rollbar.com slash changelog.
Once again, rollbar.com slash changelog.
From Changelog Media,
you're listening to the Changelog podcast
featuring the hackers, leaders,
and innovators of open source
i'm adam stachowiak editor-in-chief of changelog on today's show jared and i are talking to david
hanmeyer hansen sometimes better known as dhh we're talking about how javascript sprinkles
and basecamp evolved into a framework called stimulus we talked about the ins and outs of
basecamp as it is today. Ruby, JavaScript,
and David's somewhat newfound love for that language, how they open source because they can,
and David's new YouTube series called On Writing Software Well.
So David, I guess the thing we're here to talk about is how the JavaScript sprinkles that were inside of Basecamp turned into a full-fledged framework stimulus JS.
Can you tell us the story of sprinkles to stimulus?
Absolutely. we started dabbling with JavaScript, both in the form of sort of pre-AJAX and then post-AJAX,
and then through Prototype and jQuery and all the other libraries, we've always adopted a sort of
distance approach somewhat. That what we're trying to do is we were trying to build some html documents um that were linked together i know
this is terribly novel um but then we would adorn them sort of progressively progressive enhancement
is sort of the fancy term for it but that's right doesn't even fully describe our philosophy because
it's not so much just progressive enhancement in terms of oh your entire app should be able to
be fully functional if someone turned up javascript that't why we did it. That wasn't why I was interested in
progressive enhancement. Progressive enhancement was more a architectural technique of how to
build great applications, which is actually similar to how I came to REST and the REST principles for building HTTP applications.
It wasn't so much that I had this pure affection for representative state transfer and the
theses behind that.
It was simply because it was a wonderful architectural pattern for guiding development and guiding
how I should put an application together.
And so it is with progressive enhancement that using HTML and having the server side generate that,
and then sort of sprinkling little bits of dynamic dust all over the application.
Oh, this little button, instead of being a full page change,
we're just going to have it dynamically update a tiny
bit of the page, which is really appealing to me because the type of applications we built at
Basecamp, including Basecamp itself, lend itself very well to that style. And I think that's perhaps
where some of the differences sometimes come in, that Basecamp and applications like it,
including GitHub, that uses a very similar style,
even if we don't use the same frameworks, they have something called Pjax that was
predecessor to Turbolinks. And that idea, which we can talk about later too, they actually go
well together, Turbolinks and Stimulus. So that's part of a two-pack punch. But in any case, that applications like that, where it's not about
presenting a, let's say, a desktop like super high fidelity, super connected UI, where you're sort of
changing a little thing over here. And then there's five different other points that need
to update at the same time. That was the world we were in, right? And some people are in a different
world, right? Like if you're trying to make some sort of dashboard interface where you're tweaking little dials
and having all sorts of ramifications and updates you have a different domain and you should
actually use different solutions for it that's one of the things that long has annoyed me both
with software development in general but but JavaScript development in particular,
was this idea that there's one template for applications for the web,
and that we should all embrace React and Redux as life and savior here,
or before that it was Angular, or before that it was something else, right?
That we have all the same kinds of applications applications and they all fit in the same mold.
And I just couldn't relate to that at all. So that's why for however long it's been that we've had these sort of heavy JavaScript frameworks that used clients at MVC and tried to do all the generation of HTML on the client side, we've rejected that.
We didn't see the fit.
First of all, it didn't fit, obviously, with progressive enhancement,
but it also didn't fit with what we were trying to do
or made anything better.
Over the years, we've had our dabbles in using frameworks like that,
and it never ended up that the code was better afterwards.
One of the techniques that I'm very fond of is doing the ab you take a piece of real production code a real feature and you write
it five different ways and that just tells the truth incredibly quickly and the truth that i
derived from that sorry go ahead i was gonna say so in the process of, did you try out some of the, like an Angular or React and dabble with the Basecamp source code and do these different things?
Yep.
I tried a bunch of different frameworks.
Actually, just before going head into stimulus, I did another broad search, essentially evaluating all the frameworks that were already out there and seeing do you
know what i'm i don't necessarily want to create another framework we have frameworks coming out
the wazoo at base camp right like i have my hands plenty busy with just rails and turbolinks and
whatever is there a way that we can simply just adopt one of the existing frameworks as we actually
did um with jquery right so So when jQuery was the hot sh-
We just decided, okay, we have this other framework prototype
that we had extracted from Basecamp.
But it wasn't really different enough from jQuery.
And jQuery clearly had the momentum.
Let's just switch to jQuery.
And we did the same thing with Rails, right?
For many years, Rails shipped with jQuery in the box
because it was just, that had the momentum.
It was close enough, and it was actually a great framework.
I, to this day, still like a lot of things about jQuery, and I think its focus on ergonomics and API design were wonderful and in some ways lost in this transition to the so-called modern era of JavaScript development.
In any case – where was my train of thought here?
Did I try these things?
Was that where we were coming from?
Right, yeah, exactly.
I kind of went down these rabbit holes.
Because you're such a, you know,
you're the kind of person who has your own thoughts,
has your own viewpoints on elegance,
especially when it comes to code.
And it would make sense that maybe you wouldn't try it,
but it seems you did.
No, absolutely, because I get inspired by it too, right?
Oh, that was where we were coming from.
Whether there was another existing framework out there that could serve our needs.
And I mean, I'm sure there would be.
I mean, it's not like the most terrible thing in the world would be if we used React for some things, for example, right?
I don't have any fundamental opposition against that in fact i think react in particular had uh an incredibly correct core
insight that instead of maintaining this complicated sense of state you would just
blow away the world and re-render it every time i think that's a great insight and actually quite
similar to our views on how to do
things. It sort of didn't went downhill from there, in my opinion. That was the sparkling
moment of where I thought like, oh, there's something really interesting there. And then
I started seeing sort of the mishmash of smashing all the considerations and concerns around
competence into one big file. Like we've spent, I don't know how long, trying to separate things out such that we
can be on different rates of change and all these other good things.
And now here we go, React, HTML smashed in with code, smashed in with state management,
smashed in with everything.
And I thought, I don't think that's actually progress.
So in any case, I reviewed all of these things. And then I also reviewed the JavaScript
ecosystem at large. And I actually liked a whole lot better what I saw there than what I saw in
the particular framework expressions that came from that world. I love absolutely the fact that
ES5 onward was such a better language, especially for the type of JavaScript that I wanted to write,
which was a more object-oriented style, obviously influenced by the fact that Ruby is my true love.
And I like that style of programming where it's mostly object-oriented, and then you sprinkle in
some functional programming techniques where they make sense. But at a core, I like to structure my code in an object-oriented manner.
And ES5 forward just made that not be this horrible hoop-jumping exercise to do so, where we had to use CoffeeScript essentially to get an even halfway sane syntax for declaring classes.
So I saw all that progress in the JavaScript ecosystem and thought, this is wonderful.
And it's extra wonderful that we don't have to wait for the browsers to catch up.
The fact that we have Babel, the fact that we have Webpack, the fact that we have this
whole tool chain that allows us to use almost all of the future heading features of JavaScript today in existing browsers that people are actually using, that was what was so exciting.
So I basically took that exploration and said, all right, I've examined both the ecosystem at large and the particular frameworks.
I don't care so much for the particular frameworks.
I care for the ecosystem improvements.
What can we do here? Can I just try to get my sense of progressive enhancement,
my love of sprinkles into a more cohesive structure
that at first it wasn't even about the rest of the world.
It was just about how we write JavaScript at Basecamp.
It was about reforming that way.
Because what I had come to dislike was we had such a high standard
at Basecamp for writing beautiful Ruby code.
And then we had a very uneven standard in some ways for writing JavaScript.
On the one hand, we had some wonderful deep diving JavaScript explorations with Sam and Javon.
They've made both tricks and they've made turbolings and it's beautiful, wonderful code.
And it's really pure and wonderful.
And then we had specific support sprinkles for a feature here,
a feature there.
And we had like four different styles of doing that,
four different ways of attaching event handlers and so forth.
And someone coming in new to the Basecamp code base would go like,
which, which paths should I follow again? And then usually they just open some file that vaguely resembled what they were trying to do and kind of follow that, which wasn't great, right?
You probably had heaps of CoffeeScript code at this point too as well, right? Which actually, I mean, CoffeeScript is not one of those things I look back on with any sense of regret.
I think CoffeeScript was a monumental step forward for the syntax of JavaScript prior to ES5.
And in some ways still is, but the gap has now narrowed so considerably that I don't know if the CoffeeScript path is necessarily worth it anymore.
But it was totally worth it for those years
when all we had was ES3, right?
How are you guys managing that migration
inside of Basecamp?
Are you file-by-file converting it to plain ES5 or ES6 JavaScript?
Are you leaving the CoffeeScript in place?
What's the progress on that?
How are you guys doing it?
We're basically looking at things in sort of new horizons.
When we make something new, we will make it to the best of our abilities, to the best concepts and whatever that we have, which today is stimulus.
So when we write new features, we write stimulus update them in some way we will sort of weigh
whether the change is large enough to warrant a full conversion to stimulus or we should just
leave it in place because the thing is you don't really need to do this mass migration the wonderful
thing about having um babel and these transpilers is the fact that we can mix and match as we please
in fact we do even with modern code.
Stimulus itself is written in TypeScript, not in just vanilla ES5 or 6. And that's because
Sam and Javon enjoy using TypeScript when they're writing framework code. And I'm not a huge fan of
sort of statically typed or explicitly typed, I should say, code.
So that's not what I really want to write.
And it's not what we need to write when we write features.
But they enjoy doing it for the framework itself.
And since they're doing the work, I'm like, of course, you guys get to then pick what you want to do.
It doesn't matter.
It all just compiles down to the same thing in the end.
So what do I care whether the parts of stimulus that I didn't have to write is written in typescript or coffee script or whatever
there's something to be said for having a general style in your application code perhaps
but i mean these aren't drastically different languages right like they're like dialects
like i'm from denmark and in copenhagen we speak dialect, and I mostly still understand people from Jutland
and the dialect that they speak,
and it's actually not a big deal.
I think people have a tendency to make it
a much bigger deal than it really is.
That makes a lot of sense.
I know as somebody who has dug into the Turbolinks source code,
and I've written a fair bit of CoffeeScript back in the day,
but hadn't for maybe a year, 18 months.
As a casual contributor, a potential contributor,
I may have just opened a bug report or something,
but looking at the Turbolinks code, which is CoffeeScript,
I know that there was definitely some cognitive overhead,
some catch-up I had to do in order to read that dialect.
I think on the other hand, TypeScript actually makes,
for libraries, actually makes it easier to contribute
because of the documentation available and the types being so obvious and stuff like that.
Whereas CoffeeScript may have been a detractor for third-party contributors.
I think TypeScript might actually make it even easier for people to get involved.
Maybe.
I'm not so convinced that that's the main barrier to entry. In fact, I think that technologies have a tendency to overstate the differences
in the technology choices versus the actual cognitive
barrier there is to understand systems. To contribute to, say,
Turbolinks, there's a fair conceptual model that you have to
understand before you can contribute. I think climbing that hill
is a taller one than the subtle dialect differences between
CoffeeScript and either ES5 or 6 or TypeScript.
But I mean, to each their own.
And I think what's just great is the fact that we don't have to go through these violent
transitions where in order to have new features, for example, of our application written in stimulus that uses TypeScript written in ES 506.
We don't have to rewrite everything that we did before that, because I think those types ways, it's actually a blessing that we're using transpilers all over the place.
And it all just boils down to whatever you compile it to, whether that's ES3 or 5 or whatever your final output target is, that it just matters less and less these days.
I think that's real progress.
Since you mentioned Sam and Devon, and from what I understand, the process was, it sounds like, you know, you were doing some evaluation of JavaScript, the ecosystem.
You had done some research, and one day you prototyped something, and that kind of worked.
And then Sam and Javon took that and rewrote it into what is now Stimulus, and that's what you're talking about, which is TypeScript, is the language that that's written in. Can you kind of walk through that process of discovering this
and kind of getting to the point where you could prototype something
and what that was like and then the transfer from you to them
and how that played out?
Sure.
So as I said, I did this huge survey of the landscape
of all the major JavaScript framework.
And I read through all of them,
and I played with all of them. And I tried to see which ideas I could get inspired by.
And then I started up basically just playing with all the new features in ES5 and 6.
This was sort of the first time I had really dug back in and tried what's at the forefront of the JavaScript ecosystem
development in quite some time. And I started using all the Babel packs for all the
in-progress proposals for all sorts of things. And I just wanted to sort of like, if I could
use the latest and the greatest, what would that feel like? So I played with a bunch of different things.
At first, it was just experiments, general experiments.
And then pretty quickly thereafter, it become focused on, OK, let me review all the JavaScript we have in Basecamp.
Let me try to see if I can pick out and extract the best styles that we're already using and kind of gel that into a framework that will guide people to stick with that style.
Because a lot of what we had was basically just sort of subtle conventions of writing code,
which I'm a big fan of conventions, but I think they're much stronger when those conventions are
backed up and enforced by a framework. So that was basically what we're trying to do. I wasn't
necessarily trying to invent anything here as I usually do with frameworks. stimulus was going to be,
and wrote it in ES6 with a bunch of plugins. And there was enough there, it was sort of
stuffed out in some ways, it didn't have the full mutation of server and so forth. But
my sense of the design was really focused around like, let me take an existing piece of
JavaScript code in Basecamp that supports a given feature that I don't around like, let me take an existing piece of JavaScript code in Basecamp
that supports a given feature that I don't really like, that I feel like is messy or smelly or
whatever. And then let me rewrite it in this proposed format that I have for stimulus and
see if it's better. And I rewrote a bunch of different pieces of functionality. And I was
astounded by just how much nicer they were to work with when I had rewritten them in stimulus.
So after going through that process, I sort of chimed up Sam and Javon, who've been doing most of our deep dive science project, the JavaScript development for a long time, including both Turbolinks and Trix.
So they were sort of the natural team within Basecamp.
We call them research and fidelity to take this on.
And I basically just I wanted what I had written up to really work and not just be stopped
out and not just be a prototype, but be the thing we actually wrote new JavaScript in.
So they took this on and integrated another framework
that Sam had been working on around mutation observers called Sentinella.
And we ended up merging the two directions and it became Stimulus.
And they, as I said, they rewrote the whole thing from scratch.
In fact, since I wrote the initial prototype of the framework,
I haven't written a single line.
I think if you do a git blame on the
current repo, you're not going to find my signature on any of the lines written in TypeScript.
My role sort of just shifted from, okay, I built the prototype and I have a very clear idea of
where I want the final API to go, what the client code should look like. So let me provide that guidance
and let me help sort through some of the conceptual issues
that we then faced on how to design the API,
especially around targets and so forth.
And here we are. This episode is brought to you by our friends at Linode. Everything we do here at Changelog is hosted on Linode cloud servers.
Pick a plan, pick a distro, and pick a location,
and in minutes, deploy your Linode cloud server.
Duel-worthy hardware, native SSD cloud storage,
40-gigabit network, Intel E5 processors,
simple, easy control panel, VMms for full control running docker
containers encrypted disks or vpns 99.9 uptime guaranteed 24 7 customer support 10 data centers
three regions anywhere in the world they've got you covered they also have cloud.leno.com which
is an open source single page application find that at github.com
linode manager plans start with one gig of ram for five bucks a month or high memory plans
at 16 gigs head to leno.com changelog get four months free with their basic server
twenty dollars in hosting credit once again leno.com slash changelog.
All right, so let's talk about the architecture,
the concepts of Stimulus, how you designed it, and how it works, and how you use it.
So one of the things I really wanted with Stimulus was I wanted to solve a couple of specific problems or bad patterns that I was seeing in our Sprinkles code at Basecamp.
One of the first things I wanted to address was the notion of how do you find the elements that you want to mutate or work with? And we had a bunch of different styles. Sometimes we were using a hierarchical approach where you'd say like, oh, give me the parent of the parent
of the parent here. I know that the structure of my DOM tree is like this, and I know I want sort
of the third parent up. That's a pretty brittle way of targeting elements,
right? Like someone reorganizes things or puts them in a different way and all of a sudden you're
getting the wrong element. So that wasn't a good pattern. Another pattern that we've used was
targeting elements by finding them through CSS classes. So we'd say like, give me the elements
that match this CSS class, which on the one hand seems okay, because a lot of times the CSS classes are explanatory.
They say like, oh, this element is about the title of a person or something.
So when you query for that in the code, it kind of explains what you're trying to get.
But it's also pretty brittle. And it's also not compatible with a lot of the modern CSS styles of writing classes, which are things like BEM, that have a very particular way of writing the class names, sort of the BEM format.
That actually detracts a bit from when you're trying to use them as code identifiers, but they're very helpful on the CSS side, right?
The designers at Basecamp are really happy with BEM and the advantages that BEM affords them.
But it's brittle.
Well, it's two things.
It's ugly, and then it's brittle.
Like, it's ugly when you're trying to target elements
that have BEM classes in JavaScript code
because BEM has this format that makes sense for CSS
and doesn't make sense at all for code.
So I didn't like the ugliness of the code, and I also didn't like the brittleness of it.
But that BEM oftentimes, like you add another dash, dash, down, or dash, dash, pad, or whatever it is that you add to your BEM class to ever so slightly tweak it.
Well, if the designers were doing that, well, again, they broke the code.
So that sense of brittleness I wanted to sort of get away from. And that's
where the concept of targets came up. Basically, stimulus implores you to only find DOM elements
you want to work with through the concept of targets, which is basically just an explicit
name that says this element is going to go by this logical name that belongs to this controller.
And then we can move that name around and it's not tied to the specific type of the element.
This could be an input element. It could be a button element. It could be a span element.
And as long as it has a data dash target that's of a certain thing, you can always find it, which not only gives you this sense of clarity around
like what elements when you read the HTML code are actually used by the dynamic behavior.
And in the code itself, it's very clear when you're referencing a specific target,
what that target is and what the purpose is. It also gives you a sense of generic distance
from the specific structure of your DOM tree. As I said, a data target of, let's say, hello.name
can be applied to an input text, a text area, a span element. So you can write these generic
controllers that work with all sorts of different kinds of specific DOM tree expressions, which is really neat.
Because that was the other thing I noticed when I was reading through the Basecamp code base, that we have a lot of feature-specific JavaScript that really was quite generic in its essence. And it was only tied to a specific feature because it was naming certain types of
DOM element types, or it was specifically naming certain types of CSS. So you couldn't really
reuse these pieces of feature from one area of the application to another because they were tied to a
specific screen and a specific layout. And I wanted to get a bit more generic around that so that React does and what a lot of
competent style development does, because it doesn't afford you this sense of reuse,
that you can develop generic concerns and generic aspects of your dynamic behavior that
you can tie to any DOM tree.
So that was one of the considerations.
So that's the target concept.
That's a prime motivator.
And then the other thing, there's really only two other things.
There's controllers and then there's actions.
And actions are quite similar to targets.
They're the trickers.
So a lot of code we had in Basecamp was using sort of explicit event handling, where we would tie an event handler to usually somewhere up the tree to some parent.
And then we would sort of interrogate that event as it bubbled up, see if it was relevant for the behavior we were trying to we were using these attributes called data behavior, and then we would scan these data behavior attributes as the events bubbled up.
And then if the attribute was a match, the data behavior was a match, we would trigger the behavior.
Well, that does not provide very readable code always, I'd say, that sort of sometimes we would provide a data behavior
on a parent element, and then there would be specific DOM elements inside of that parent
that would trigger behavior, and we would catch these events, and we would do something.
And one of the things I really disliked about that was I wanted to get to the point
where if someone opened up a piece of HTML, they could see what was going on, that it didn't have this shadow land of event handlers living somewhere in some JavaScript files far, far away.
So you couldn't tell what the HTML actually wanted to do.
I wanted it to be explicit such that, hey, if you click this span, you click this button, you know what's going to happen.
So that's the concept of the actions, that we make those explicit in the HTML itself.
We don't hide them away as event handlers in the JavaScript code.
I mean, underneath there's an event handler, and that's what stimulus provides you.
It provides sort of the plumbing to do that.
But what I found was a lot of JavaScript code was very,
let's say low level. It was very sort of, yes, event handling is the way that we process this
way. And we deal with the with the UI, but that doesn't have to be the way we actually write it.
And it certainly isn't the way, the best way to provide understanding of how a system works.
So now we have, you can do data dash action on a
button or a span or an input or a submit button or anything else that you want when the user clicks
that or submits that or hovers on that or something else, some other sort of event trigger, you can
trigger a specific action and that's spelled out, right? So it's data dash action equals, for example, click points to hello, add sign greet.
So you read what that button does, right?
You don't actually need to read the code.
You can read, okay, if a user clicks this button, I'm going to call the greet method on the hello controller.
That's super explicit.
And it means that most of the time, I don't even need to look
up what the code actually does. I don't need to look up the underlying JavaScript controller to
understand what actions are available on this view. And I found that that was just so liberating.
The second part of that was that just like data targets, the actions were generic and they could
be moved around. So if we currently have an action on, let's say, a button and we move that action to a link, the controller could remain unchanged because the controller didn't actually care or know what type of element invoked its functions. quite different from when you marry the HTML structure with your component and with your
dynamic behavior, then you're kind of, you're locked in step and you can't reuse these things
and you can't move them around. So that seemed like a big advantage. And then finally, the last
concern or the last concept is this notion of the controller, which is basically an encapsulation of
all the behavior that relates to one aspect of one
feature of the system.
And that's where we basically just use JavaScript classes.
And these classes have methods on them.
And these are the methods that we call through the action triggers.
And those actions and the methods that they correspond to interact with the targets that we've named.
And that's it.
There's three basic concepts.
And it doesn't take a long time to learn it.
Even without learning it, you can read the code, you can read the HTML structure, and you can understand what's going on.
And that's really all we needed.
In fact, I was kind of shocked when I first started extracting this stuff.
I was kind of thinking, oh, man, there's going to be so, so much stuff here. And it was the real epiphany of seeing that those three basic
concepts, the controllers, the targets, and the actions were enough to extract such a wide body
of behavior from Basecamp. Going back to the targets for a second, I guess I have a comment and then a question as well. So I between like selectors class names you know dealing with the design side dealing with the functionality
side and so like what what i've been doing for a long time is just using like a js dash prefix
on a class name and saying js dash and then you're basically doing a target and that is then a signal
to the designer that okay this this class does not have
to do with the look and feel this is a js specific thing it so therefore they won't get changed based
on someone trying to change you know the way something looks and then secondly it also signals
that this thing has some javascript attached to it somewhere because like you said we've we've
detached the handling to you know someplace in the
sky um and so there's a little bit of a signal there but um i like the data dash um way that
going about it there's no drawbacks and the i guess the question comes in when it comes time
to actually modify things you mentioned bem and the problem is you know you get class names
pulled out from underneath you or what have you.
These are issues.
And this is genericizing that and pointing it out of the class name, putting it onto a data attribute.
But how can you genericize it when it comes time to actually modify the elements?
For instance, this click handler actually hides an element or changes it to a variant that's a larger version or a
smaller version, aren't you still dealing with class names in terms of the current look and feel
of elements? Or is there somehow that you guys are also genericizing that
so you don't have to worry about the BEM class names in your controllers?
I love this question because this is literally sort of the fourth concept that we'll be introducing shortly and that I've been working on for some time.
It's exactly as you say. We do almost all of the mutation of existing elements through classes, whether we want to hide something.
Well, that's having a class that hides it or we to show something, or we want to play an animation, we use CSS animations.
Classes are really the way to mutate the DOM
the vast majority of the time.
And those classes are explicit, right?
Like they're BEMed or whatever they are.
And you shouldn't let those BEMed class names
leak into your code
because you don't want your designers
to have to open up some JavaScript controller
just to
Change a BEM class to add a bit more padding, right?
so what we're going to basically do is is a
similar construct where you can
declare a data attribute that includes the
BEM class in the HTML
Which is the is where the designer would be changing it around. And then you can reference that BIM class in your code and say, hey, I want the CSS class that's for hiding.
Give me that class. And then apply it to this element such that we return it to this sense of
genericism. And we're going to be working on that next. I'm hoping that stimulus 1.1 will include the abstraction of classes into this structure.
So we can use logical class names that make sense for the code rather than concrete class names that make sense for the designer, that adhere to BEM, that do all these things.
Does that mean that the class name will get set through a data attribute?
Exactly, yes.
So there will be somewhere on the, for example,
on the controller, so they can be changed
by the designer in the HTML.
So the designer does not need to monkey around
with the controller, at least as long as there's
a stable sort of set of logical class names.
If they add additional class names or whatever,
you might still have to have some involvement. But many cases especially with bem right bem has this idea
that you can mutate and you can combine a single presentation of an element through mutation of
one class name right and that is is really something we want to abstract and get away
from like there should not be bem class names inside of a controller they should be logically
referred to it would be rad if there was some sort of declarative way that you could set that
all up up front maybe in one place where you could map these things you know whether it's just a map
in your javascript code or something where it's like these logical representations,
because there's a handful of them.
Like you said, you could cover 80% with defaults.
And these map to these particular class names,
just so that the designer is not necessarily having to add data dash,
BEM or class name to all of the elements all throughout the HTML.
Well, you only have to do it for the CSS classes that you need to dynamically apply.
Remember that.
Most of the CSS classes you don't need to do this work with. It's only for the ones you need to dynamically apply to something like if you have a specific
class for hiding an element or something else like that.
So I don't actually think that in most cases you can go generic with them,
especially if you follow BEM,
that a specific presentation of one feature
might have a logical class name for hiding things,
but the concrete implementation in BEM
for hiding something might be slightly different
than it is somewhere else
because it adds or removes padding
or margin in some ways.
So we're still sort of feeling that out for now.
I'm pretty confident that a huge step forward would simply be to go abstract
with the CSS classes you need to dynamically apply
and then declare them either in the same element that holds the controller name
or on the target or somewhere else where it makes sense.
Just disconnecting these two things
so we get on two different trains of changing them.
It's interesting that, Jared, your version and David's version
is essentially not the same, but it's very explicit.
Like you were prefixing JS and David's solution
is reusing the concept of,
of data attributes.
And again,
being explicit of saying this is different.
This is not your normal class.
It's for a special purpose.
Yeah.
I think it's,
it's coming from the same motivation,
right?
Like we want to be able to read a piece of HTML and know what it does.
We don't want it to sort of just magically have things happen to it
because far, far away there's some JavaScript class
that declares an event handler that just happens to match this thing.
We want the explicitness and we want to be able to read it.
I think that's just good code practice.
So whether you do it one way or another,
as you found, there's just a clear motivation to solve that problem.
Yeah, and I think it was trying to get back to something that we lost when we went from writing your click handlers right there in the HTML, which is decidedly way too low level to be in your markup to now we're going to put everything into jQuery click handlers or what
have you, is that we lost that connection. And now there's a lot of people rightly saying,
hey, this is actually a step backwards because now you have like random things happening that
you don't know about. There's no connection in the code. And so this kind of bridges that gap
and puts a nice, happy nice happy medium in place.
Which really comes back to why are we doing stimulus?
And why aren't we just picking an existing framework that's out there?
There's certainly plenty of them in JavaScript land already.
And I think some of it is because I just saw regressions. I saw that we took certain steps forward in certain domains
with this new set of JavaScript frameworks.
And then we took huge steps backwards in all sorts of different ways that apparently people
just didn't care about.
And that's fine.
I mean, we don't all have to care about the same things, but I cared about those things
and I couldn't live with those regressions.
So stimulus is a way to sort of not take those regressions, get back to, in some ways, a simpler time, which is, I mean, it's always dangerous once you get into arguing what's actually simpler and what's not simpler.
Because a lot of it is tied up into, exactly.
First of all, good old days, right?
Good old days were often not that good.
And second of all, just that we have different applications
and they work in different ways.
If you're trying to make sort of this very intricate UI
with tons of related things and blah, blah, blah,
then maybe these heavy frameworks do make sense.
If you're trying to make an application like Basecamp or GitHub,
they don't make sense.
And they are overly complex for what we're trying to do.
And I just felt like there was an underrepresentation of frameworks that were trying to tackle the
class of application that GitHub and Basecamp finds itself in, which is not a small class.
I would argue, in fact, that it is the larger class class and it's been severely underserved by this um recent advent and
explosion of javascript framework that all focused on the same paradigm that the server side was now
just tasked with producing json and then you would have some client side engine that would take that
json and turn it into html build up a DOM, whether virtually or otherwise.
And all the frameworks are just the same, right?
Which is why I was sort of struggling a bit when there's such furious wars between like, oh, are you using Vue or are you using React or using Angular or using whatever?
If they all come through the same paradigm of server-side generated JSON that's then dynamically translated into HTML.
I mean, come on, guys. It's the same idea, right? Like we're really furiously arguing about the
small details. Stimulus provides a completely different paradigm, a paradigm where the server
continues to create the entire HTML document. And then we sprinkle this remaining behavior that we
need onto this through a progressively enhanced approach.
But even when we do dynamic stuff, so we have stimulus controllers, for example, that will trigger a behavior on the server side.
What the server side will return is a fragment of HTML.
We use HTML as the transport protocol very rarely, although sometimes we will use JSON to do it. So when we want to
update a part of the page, we ask the server side, hey, can you give me this fragment of it?
Which allows wonderful things like fragment reuse, which if you're familiar with Rails,
we call those partials. And the fact that you can use the partials, the fragments of the HTML,
both to render the initial version
of the page and the subsequent updates is a huge step forward. That's interesting.
I'm curious about this. I know that in Ruby code, David, you like to remove comments and
rewrite code or replace comments with more readable code. And in your process of evaluating Basecamp
and looking through things of this transition to stimulus,
I'm curious how much of that happened in your code.
How many comments were joyously removed
in replacing with this prototype version that you created?
A fair amount of it.
And I think that that is exactly the attraction
to adorning the HTML with these very explicit tie-in with how things are
called because when you have a button that says it has a data action where if you click this button
we're going to call greet the greet action on the hello controller that's incredibly
self-documenting you do not need a comment, a code comment to
explain what's going on. And on the controller side, the same thing, right? When you have a
declaration of action methods up front, you don't need to comment on how those are hooked up. You're
relying on the conventions that stimulus affords you. And those are already documented in stimulus.
So we can really cut
down on the amount of needless documentation that we need in our application code, which for me,
whenever you have copious amounts of code common, it usually tells me two things. Either that you
wrote convoluted code, that's probably the most common. Two, that you have a set of conventions
that you're just still waiting to extract.
And then once you extract these,
you can remove all this repetitional commenting.
And these are sort of the driving motivations
for why we want to get rid of them,
why they're code smells.
I mean, in some rare cases,
what you want to do is actually just counterintuitive.
And sometimes it's basically around browser bugs or something else. In some rare cases, what you want to do is actually just counterintuitive.
And sometimes, especially around browser bugs or something else, you're doing something that does not make sense if you just read it.
And you need a code comment to explain, you know what, that's because IE Edge does something stupid here.
And this is why we need to do this monkey dance to make it happen. But even in that case, I often find that you can still encapsulate that monkey dance in a method that succinctly explains that it's because
Edge or Safari or Chrome or whatever
is doing something that you need
to have special consideration for. This episode is brought to you by Gliffy.
Gliffy is the easiest way to visualize any idea.
In a world where it's possible to draw a circle in the cloud
or put a square to a circle with some text in it,
it lives a tool with over four and a half million users
who easily diagram every single day online
or directly
in Atlassian using Confluence or Jira. And in this segment, Craig Cockrell, product design lead at
Plastic, shares how his team lives and dies by Gliffy. For me, using Gliffy is really nice just
because I'm able to really not have to worry about the tool as much. I can sit here and really focus
on what I actually
want to accomplish. The way that I've described it to other people in general is it's something
that's very hands-on. It feels as though you're really using very material objects and rearranging
them on the fly and making that movement from taking this screen and moving it towards the end
or reconfiguring that flow. It's not something where you really need to feel the pain of going through that.
It comes so naturally that, you know,
the tool is almost secondary.
And what I'm trying to focus on and get done
is the primary goal.
You know, ideas live or die on the diagrams
that people create in Gliffy.
If you're not creating that fidelity,
it's something that it's very easy for that
to just literally fall off to the wayside,
you know, within the company here at our core, a startup, right?
People have a lot of ideas and they have a lot of things, but there's obviously gaps
in communication and without something to actually bring to people and bring to the
table and show that you've done some level of work, you've done some sort of background
in a gliffy diagram is one of the best things that anyone has ever brought to any sort of
product inception meeting or anything like that. And that is truly where we can start that collaborative process without that that
conversation doesn't happen the right stakeholders don't get involved and at that point you don't
have anyone rallying behind you and without you know the group rallying behind a certain project
it just doesn't happen when anything begins it's so important that those individuals open Gliffy, in Confluence, get in there, start working, and just get it started.
It's just something that allows a canvas for us to create.
You can try Gliffy today for free, online or in Atlassian, using Confluence or Jira.
Start building ideas today with your team, and if you don't have any ideas to make, draw a duck, set it as your desktop wallpaper.
Get started at gliffy.com slash changelog. All right, David, so one of the, you said what you saw was, you know, a lot of regressions
and maybe even sidesteps in certain cases.
One of the things I remember back in the day when Backbone.js first hit the scene, because
this was one of the very first, you know, front-end frameworks that said, specifically,
they said, get the state out
of the DOM, right? Get it into JSON, get your state out of the HTML. And that was a step.
And everybody started doing that in different ways. And so we became, you know, completely
detaching the front end and the back end. And like you said, Stimulus offers really a different opinion or a different way of going about building applications than a lot of the other front-end frameworks out there.
And one of the things that it says on the homepage is state is stored in the HTML so that controllers can be discarded between page changes but still reinitialize as they were when the cached HTML appears again.
So that's definitely a big difference from other things out there.
Can you tell us about how that state is stored, how you deal with change?
I know you mentioned a little bit during the last segment,
but let's go a little bit deeper into how that all works.
Sure. So we store state in much the same way that we declare the targets and the controllers and so on through data attributes. And we basically just set these data attributes on the usually the root element of that controller. And that stores the usually minimum amount of state like i think one of the reasons um backbone and other frameworks have
argued for extracting state into something else is because maybe they they had a ton of it and
we try not to have a ton of it we try to have very little state between uh or within the the
dom itself but just enough so that you can reinitialitialize a controller and it can come back to the form that it was.
For example, a state could be, we have a collapse controller that allows you to click a certain
element to open or close another element, right? You know, like show more or see less or whatever.
And the state of whether that is open or closed is something you can store in the DOM.
In that particular case, oftentimes the state is actually just an application of the classes.
Classes provide usually most of the state that we need.
For example, for the collapse example, we will apply a hidden class when the element is closed, and we will remove that hidden class when the element is open.
So that right there will store the state in itself.
We try not to duplicate or create a shadow state of what the DOM already has, because we're trying to enable you to get HTML from anywhere, right? That most of the time, this HTML is coming in the form of the initial render, and the
controllers simply have to take that initial render and instantiate themselves based off
that.
And then if you have other updates, it could be WebSocket updates that you have a WebSocket
channel that inserts new HTML into the DOM.
When that new HTML is inserted, it needs to include its own state.
We don't have something else to also pass that state along with.
As I said, we use HTML predominantly as the transport layer, not JSON.
So the transport layer has to include its own state,
and that's what we found.
This is actually a nicer way of doing it.
And then just the fact that we go back and forth between pages,
we're using this together with Turbolinks.
And Turbolinks stores a nice cache of the pages we've been switching between.
So if you have controllers that have been mutating the DOM,
usually just by adding CSS classes or
whatever, Turbolinks will remember the state of that when you go from one page to another.
So we get to sort of keep that state alive through the Turbolinks cache. And that's adequate
and actually a useful, I think, constraint in most cases to not go hog wild with a whole bunch of intricate state.
I suppose it works just fine without turret links.
You just don't get the advantage of the fast refresh.
You get a full page reload.
Yeah, and you just got to be a little careful with those full page reloads and what the browser actually ended up caching,
whether it ended up caching the final version of what the DOM looked like, or it'll reinstate it from scratch.
That's just Turbolinks helps a little bit there in terms of making it more fluid and
making it easier to keep that state cached.
So were there specific changes that went into Turbolinks to support this?
I know you call it a one-two pack punch or something like this.
I was wondering if that just happened, they just paired well nicely, or if Turbolinks to support this? I know you call it a one-two pack punch or something like this. I was wondering if that just happened, they just paired well nicely, or if Turbolinks required
some specific upgrades or enhancements to actually support stimulus natively.
It didn't because we were already basically writing stimulus before we were writing stimulus,
right? Like this approach of using progressive enhancement and storing state in HTML and using HTML as a
transport layer. That has been our pattern and our paradigm for a long time. And we built
Turbolinks originally with that paradigm in mind. So stimulus is basically just an encapsulation of
that paradigm and packaging it up in a nice way. And that was really the missing second punch to
Turbolinks that we would pitch
Turbolinks to someone like, hey, this is this wonderful thing that can actually cut out like
80% of all this dynamic behavior you're doing because it'll speed up the page changes to such
a degree that you don't need anything else. There's a lot of dynamic behavior we're doing
for performance reasons because it feels too slow to do a full page change that you no longer need
when you're
using turbolinks because the page changes are really fast so you can just send the whole page
again even though you're making a relatively small change in a lot of cases but then there's still
the last 20 percent where you didn't want to do that right like you had some small change like a
collapse show thing as we just talked about it It's a little excessive if showing or hiding,
which is basically just applying or not applying a CSS class to an element,
required a whole round trip to the server and sending down all the HTML for a whole new
page just to apply a single additional class. That doesn't make sense. That's not proportionate.
And it's not going to be fast enough to feel really good. So that was this missing gray land,
the last 20% of behavior,
where we kind of just waved our hands and said, have you looked at mutation observers, and then
left it as an exercise for the reader, which was actually a fairly large task for someone to do.
So I could see how not having a clear answer for that last 20% held back Turbolinks in some ways.
So I'm really happy that we now have stimulus to provide 100% of the answer
for applications like Basecamp.
And if you want to write them in this way, you now have all our tools, right?
Like there's nothing hidden under the carpet here, right?
Everything that we use to write Basecamp the way it is today
is open source packaged up as an easy to use library or framework.
And the story is now complete.
And I think that that's really important that if someone is looking at their application
and they can't visualize how they're going to solve this specific part of it or this
specific feature, it's hard to gain adoption.
I think Turbolink suffered for some time with that.
It also suffered for other reasons. For example, this notion that apparently it was hard to understand the concept of a persistent process and that you couldn't just drop in any jQuery plugin that was written with the idea of every page change blowing the process and the instantiated application away even though that was exactly the
model that people have been following with single page applications and the heavy frameworks right
like they all run in a persistent process where every right click of a link is not a full reload
but for whatever reason i think a lot of people just saw like hey i should be able to just use
triple links and any odd jquery plugin that I can find on the Internet.
And those things need to just magically work together out of the box.
And they sometimes just didn't, right?
You had to do special considerations to deal with the fact that Turbolinks does not change the full page, right?
It gets a whole HTML document, but then it does the updating of that document in process.
And it keeps the state of the JavaScript around and it keeps the state of the javascript around and it keeps
the interpreted css around and that's where it gets its super speed its turbo speed so at base
cam exactly at base cam we just didn't use a bunch of javascript or a bunch of jquery plugins we wrote
most of our code yeah um that we needed ourselves and sometimes we used used some jQuery plugins and we just altered them
a little bit since they were compatible with Turbolinks. And that never seemed like such a
big deal to us because the benefits that we got back were so monumental. And what it allowed us
to do, especially in terms of the majestic monolith and applying a simple application that was
automatically updated across five different platforms, which is such a huge win that was automatically updated across five different platforms which is such a huge win
that was really hard for us to imagine that anyone would look at the small changes or concessions
that you have to to make to get those wins and go like oh yeah that's not worth it so anyway i mean
yeah turbolinks and stimulus they're frameworks we're writing for ourselves. I'm doing these things
because we need them in Basecamp. And when someday the day is going to come when I write Basecamp 4,
I want them to be available in packaged and clean form. So I can just use these
frameworks off the shelf and get on my merry way. And if someone else ends up using it,
that's great. And if not a lot of people end up using it, that's also great.
And we will continue to do it. It's sort of the same approach I have with Rails. When I originally
wrote Rails, I wrote all of it, right? I needed a way to talk to the database. Well, I wrote
ActiveRecord. I needed a way to render templates. Well, I wrote ActionView. So at Basecamp, we have a tradition of writing our own
tooling. And then we share our tooling more out of sort of gratitude to the rest of the community
for the tools that we do use. And just because that's a nice thing to do. And sometimes it ends
up taking off, like as in the case of Ruby on Rails, and we get some benefits from that. And
if it doesn't take off, that's also fine, and we get to use them.
I think that's really a sort of ambivalent or distant relation to the open source process,
that we open source because we can, and because we like it, not because, oh, it has to gain
adoption.
Right.
Just hypothesizing a little bit about Turbolinks, because I've been around, I think, for the Right. That problem with existing jQuery plugins and the fact that Turbolinks was such a plug-and-play
aspect of a Rails application,
you could literally just comment it in or out
and it would or would not do everything for you
in terms of Turbolinks itself.
It was so easy just to flip it off.
Pun not intended, but perhaps maybe it should have been.
So easy for people to just use Turbolinks as the scapegoat.
It'd be on.
This JavaScript issue doesn't happen.
Oh, it must be Turbolinks.
And so it just got a bad name right off the bat
just because of the existing ecosystem it came into.
And then it's kind of like the Siri situation.
People just don't, you know, it gets this reputation up front
and then over time people kind of just naysay it or don't give it a second look.
But maybe through stimulus, people will give TurboLynx a second look.
Maybe not.
But like you said, you're ambivalent to it.
Well, I think also part of it is you have to understand what it's doing for you, right?
And I think perhaps that was the drawback of having it be so easy to turn on and be included by default is that if you don't fully understand the benefit that you're getting, you don't understand the tradeoff.
And if you don't understand what benefits you get, any cost is too high.
If I perceive the benefit as nil or nothing, then if I just have to pay just a modicum of work myself to get it, I'm going to
say like, that's not worth it for me. And I think we're perhaps getting to the point where more
people are realizing that using these heavy duty client side frameworks, oh, wait, they also have
costs. And they also have, in many cases, towering complexity. And I think we're getting some veterans that are coming out of that process that go like,
you know what, if I'm going to build another thing, I'm not going to do it like this again.
That was just painful.
And I think that pain is exactly what we try to address, right?
And until you've suffered that pain, I don't think you can fully appreciate the salve that we are offering, right? The bandage
that we're offering, which in many ways was the same way that Ruby on Rails came to be,
that there was so much pain that a lot of people had experienced using Java frameworks or PHP
without any frameworks, that they were very in tuned with the pleasure that Ruby on Rails would bring them because they knew the pain.
And I think until you know the pain, you don't have space in your brain to appreciate or even properly evaluate the solutions to it.
So I think everyone should go and build a full application in the heaviest duty of JavaScript frameworks,
regardless of whether their application warrants it, just so that they can suffer through it on their own skin and come out scarred and
battered on the other end and go like, eh, you know what? Maybe there's a better way.
That's the bitter and the sweet, right? You can never really understand the sweet goodness of
the chocolate bar unless you've had that nasty piece of candy after dinner or whatever. Like,
you got to have the bitter to enjoy the sweet.
That's right.
Yes.
And I think that that goes for all sorts of learning.
I think that's why lists of best practices, for example,
divorced from the pain from where they arose,
often don't make a whole lot of sense if they don't stick.
People aren't ready to internalize lessons until they've encountered
situations that really demanded those lessons in flight.
And I think that that's – I mean I think that's just part of the learning process, that you can't appreciate everything up front.
I think plenty of people end up going off to college and they end up having all sorts of courses in philosophy or whatever.
And they can't apply them to their experiences or where they are in
life and they go like oh this is worthless like what's existentialism about like that i can't use
that for anything and then a decade later they go like oh wait a minute um let me hear what camus
has to say about the meaning of life because they're a different station in life and i think
there's a lot of technology that works like that,
that it doesn't really reveal itself until someone has suffered through the
long road.
Some of that goes to teaching and documentation.
And one of the things that we is very difficult to do through read me's and
docs and even blog posts, blog posts are a little better,
but they're so hard to find over time is like,
what were the circumstances in which this solution came to be and why does it exist?
Which is some of the gaps that we try to fill with a change log and shows is conversations
with the people to give that historical context because, you know, there are no panaceas,
right?
There's no silver bullet and all of these have trade-offs and all of these have
reasons why they were created. And so if you lack that historical context, as somebody who's coming
to a stimulus or coming to a React and just picking a tool off the shelf based on the readme,
and you don't understand the historical context in which those tools were developed and why they
exist, then you're basically doing a coin flip and you don't know how you can apply it to your
given circumstances. So it's tough. Like you said, sometimes you just have to live and learn.
You have to just go through it and realize it. But I think we can work together to give
these historical contexts to people. And so they're more equipped to make those decisions.
And I think that's spot on. I think there's so much technology that's presented just as the how,
not as the why. And it's that why that gives us the context to evaluate whether this is a good
fit for us. Can we see ourselves in the person who developed the solution? Can we see ourselves
and their troubles in our troubles? And that was
one of the reasons why when we introduced stimulus, we did it with a document called
The Origin of Stimulus, which basically walks through, why did we extract this? Why did we
make this? And tells the story of the Basecamp code and our journey of making a majestic monolith
and how we use Turbolinks together with stimulus, why the concepts make sense,
which what are the problems as we talked about here, right?
Like the problem of using CSS classes for targeting it's brittle.
It's all these other things. It's not just like, Oh,
here's how to do that. Right. And I think that that's often missing.
And I think sometimes people sort of evaluate things from the wrong perspective.
One thing I've heard a lot of times is like, oh, I want to use React because Facebook is using React.
So it's got to be good enough for me. Right. Right.
And I've heard that with a lot of other pieces of technology, big companies using X.
Thus, it must be good enough for me. I actually think it's often the exact opposite.
A lot of the patterns and even outright technologies that large companies use are
the worst thing you could pick when you're just starting out or if you're a single developer or
small team, because these things are designed to work with much larger teams and much larger
companies with all sorts of different considerations and specializations
and a stomach for a different level of complexity when you're trying to serve a billion and a half
people um you just have different problems than what we're trying to solve at base camp we're
trying to solve sort of servicing a few million people at the most right and different orders of
magnitude and the correct and applicable patterns
and practices that are relevant for someone trying to solve for a solution space of a few
million people or less, just very different from the kind of people like Facebook, who have tens
of thousands of people working on the product and are trying to solve for a billion and a half.
And I think sometimes people just get enamored with this, oh, I wish I was Facebook.
So if I just start using their tool set and their mythology, maybe I'll become Facebook.
No.
Do you know what?
If you looked at any history of actual Facebook, do you know what their code original looked
like when Zuckerberg wrote it?
I don't think there's any of those practices left anymore at the company, right? Because they evolved and they turned
into something else. But if Facebook had started out with the heavy-duty patterns
and practices and methodologies that they're using now, if Zuckerberg
had had to do all those things as just developer one,
Facebook would never have happened.
That's right. I have a counter to that, though. Twitter used Rails.
And many people use Rails.
So I think Twitter is a great example, a great scarecrow,
a great reminder that there's so much more than technology
to whether someone fails, even technology-wise or not.
Twitter, in the early days of the fail whale, blamed Ruby on Rails and its trouble because
it was much easier to blame an external vector like Rails than their shitty architecture
for why the site kept falling over.
And then just recently, a few days ago, Twitter or some former executive from
Twitter threw an article in Vanity Fair blamed Ruby on Rails on the fact that Twitter, 10 years
into its existence, has still not dealt with harassment and abuse in a proper way on Ruby
on Rails, which is just wonderful, right? Like it's a wonderful anecdote of how humans are so desperate
to diverge and deflect blame
and accept responsibility for their own actions.
And they're just trying to find any scapegoat.
It's all your fault, man.
Yeah, exactly, right?
If you hadn't released Ruby on Rails,
they would have never had this problem with harassment.
And actually, that is true, right?
That is actually true in some sense,
because maybe Twitter would never have existed,
or it would never have taken off,
or it would never have gotten done in time,
or they would have run out of money or something else.
So in a way, I think Ruby on Rails is implicated
in the harassment problem at Twitter,
because Ruby on Rails helped Twitter get started
and helped Twitter get off the ground.
The fact that maybe it's been 10-plus years
and they've still done so poorly at addressing the fundamental problems of harassment and abuse on the platform, maybe that blame falls elsewhere.
But anyway.
Let's move into something, I guess, a bit more promising.
So you've had your hand in, obviously, writing frameworks.
We know that.
You do some great writing on Signal vs. Noise.
You've got a podcast. You've written books. You've done all these. You've raced cars.
You got kids. You're like everybody else, right? You got all these cool things. But
next up is YouTube for you, right? You got this cool new channel. Maybe not everybody's heard
about yet, but I've been enjoying it. And one thing I think is pretty interesting is that you
get this chance to essentially sit down with you and look through the Basecamp code base.
And you're just sharing all the reasons why you've done what you've done.
Can you take a moment and just kind of share what your plans are with that channel?
Sure.
So the channel is called On Writing Software Well.
And it's just just me opening up an editor and taking a topic that could be testing or callbacks or whatever
and showing how we use that in Basecamp
and showing how we use Rails and use Ruby to solve the problems.
And what I wanted it to feel like was
if I sat down with another programmer
and we just looked through some code together.
I always love doing that because I find that many programmers
when they're talking in the abstract about code and patterns and so forth, they have these fierce
arguments. No, this is the wrong way of doing it. This is the right way of doing it. And then if you
sit down with them and you look at actual code, you end up agreeing way more often than not because
the pressures and the concerns of a specific piece of code guides
most reasonable people in a similar direction, at least when they have somewhat of a shared
background and experience.
There may be functional programmers who are like, oh, anything object-oriented or side
effect Latin is wrong and whatever, and you're not going to find common ground with them,
perhaps.
But for anyone who exists in the same paradigm and somewhat of
shared beliefs, if you look at concrete code, we end up sort of liking the same things a lot of
the times, a lot more often than if we just argued about it in the abstract. And this is one of those
lessons that I've learned time and time again. Ruby on Rails back in, I think, 2009 merged with another Ruby framework called Merb.
And Merb was born for a lot of different reasons.
And some of the reasons were that the people behind Merb cared about different things than what I cared about.
Not that I actively didn't care about them.
They just weren't top of mind.
There were some extensibility concerns that they had and some performance concerns that they had.
And we thought we had these fundamental underlying philosophical differences about how to write
a framework in Ruby. So I sat down with Yehuda Katz in particular, who was one of the
guys involved with Merp at the time. And we had these fierce debates when we were just chatting
in Campfire. And then we sat down and looked at the same piece of code and went,
oh yeah, we believe the same thing. And we're like, wait, what?
We were just arguing our heads off in opposite directions.
And then we looked at a piece of code together and we came to the same conclusions.
And I've done that so many times now that I believe that that is really the
primary way you should be arguing code patterns and principles is by looking at actual real production code and doing A-Bs.
Let's write it your way.
Then let's write it my way.
Then let's see if there's one of the other ways that's the best or more likely that there's a combination of the two ways that turned out the best that we both like.
And I find that that happens just all the time, right?
So I wanted the YouTube channel to have that feel
as though we were sitting down at the keyboard together
and looking at code together
and coming to similar conclusions.
That doesn't mean, I mean, you're not going to just,
everyone is not going to sit down and watch these videos
and go like, oh yeah, I would have written it
exactly like David would have written it.
But at least if you hear again, the why,
why we wrote it that way,
how we weighed the trade-offs and came to certain conclusions, you'll understand why we did it the way that we did. And I think that that understanding is often sorely missing when people are talking
programming and talking shop. And that's why they get, the arguments get so heated, right?
We start having these violent disagreements that in many
ways are completely unnecessary or completely unjustified by actual code. And it's really,
it can't be solved and it can't be addressed just by looking at example code. It can't be
addressed by the stylized, idealized version of what programming looks like, because until you
have all the real constraints and pressures of production code, you're not taking all the complexity into consideration. And
oftentimes, that's exactly what tips the scale as to whether to go one way or the other way is to
look at the real code, right? For example, I did an episode on globals. Rails 5.2, which is just
about to be released, has a new encapsulation of globals for dealing
with things like current user and current account within the sort of lifecycle of a single request.
And if you just sat down and had an abstract conversation about globals with a programmer,
most programmers would say, well, I learned or I heard or I know that globals are considered
harmful. Why are you using globals? Globals are terrible or I know that globals are considered harmful.
Why are you using globals?
Globals are terrible.
Don't use globals.
Are you a bad programmer?
Are you terrible?
What's going on here, right?
And then you sit down and like,
yeah, all those things have strains of truth in the aspects that globals are dangerous
and you do need to be careful.
But hey, let's look at this code.
Let's look at how much simpler
and easier it actually becomes to understand once we use a global, right? do need to be careful. But hey, let's look at this code. Let's look at how much simpler and
easier it actually becomes to understand once we use a global, right? It's not a thing you should
use all the time and in all circumstances, and you can certainly get carried away with it. But
in this particular instance, using the feature on this particular aspect of the code, it gets better.
This A is better than that B.
And I think that those are the sort of concrete trade-offs,
as I said, that are really just fascinating.
And I think open people's minds much more than just a blog post that says, global is considered harmful.
That's one thing I've really appreciated about this series with you
is that you explain your preferences.
And then you look at the code and you know, your preferences and then you look at
the code and you say, this is why I'm breaking my own rule. This is why I'm putting these methods
out of order in the private method rather than, you know, as a table of contents like you normally
would. This is why, because it reads better. So I'm willing to trade off and, and you go against
the, my typical grain for these reasons.
And it's like you kind of get a chance to step into your mind,
watch how you program an actual application called Basecamp.
You're looking at actual Basecamp code.
I think it's a pretty interesting concept, and I'm glad you're doing it.
That is really, I think, the pivotal thing, the nugget, the justification of it.
The fact that there are all these principles and patterns
and best practices of how you should write software. And in isolation, they all make sense.
But when you write a real application, usually all of them disagree. There'll be one pattern
that tells you to do things this way, and then there'll be a best practice that tells you to do
things another way. And you have to weigh these things and consider which one is more important in
this particular instinct and which one will i put more weight on and that's where the wisdom is
hidden i find that a lot of programmers apparently seem contented to just learn the recipes right to
just learn like oh i can recite all the patterns i can recite solid i can recite solid. I can recite law of the meter.
And then they don't really know what to do when these principles are in conflict with each other.
And that is really where actual code is written.
It's written in conflict.
It's written under one pattern and one consideration pulling in one direction and another pulling in another direction.
And you have to carefully weigh, and sometimes subtly so um which you're going to put more emphasis on yeah let me just uh quick i agree
with everything adam said about the show quick uh suggestion and or feature request for upcoming and
this might be a little bit harder for you to organize but it'd be cool if you would sit down
with sam or with javon or somebody who's somebody who has a different opinion about even Basecamp code
and you guys could talk through a refactoring
or talk through things where it actually is a dialogue.
Might be an interesting alternate style format for that show.
A pair of programmers.
Yeah, I'd love to do that.
And I think that is basically what I'm trying to do, right?
Like I'm having a dialogue somewhat with an imaginary programmer
sitting next to me and the two minds of
my own in my head.
I've produced five of these episodes, I don't know, two hours of content or so on
in a week. The reason I can do that and the reason
I can churn these things out is because I do one take
and I just flip on my browser, I load up a couple of tabs of code and then I'll just freewheel it.
And that is crucial to why this is
happening. If I had to put in the sort of diligence and preparation
work that perhaps it would take to include multiple participants,
I just couldn't do it at this velocity and perhaps it'd be
harder for me to maintain the stamina to do it at all.
And I think that's why it's kept me back from doing this before,
because I thought, like, it's going to be a big production,
it's going to be a big thing.
No, I mean, most of these episodes have, like,
literally five to ten minutes of prep work
where I'm picking out the files I want to talk about,
and then I hit record, and then I push stop when I'm done,
and then that's it
yeah it is a trade-off in a good way it's a pretty simple production process for you
I think production process is even a very fancy word for I mean I'm hitting record and then I'm
hitting stop and that's that's it there's not a lot of post-processing or or second takes
gotcha is there anything else to share about the future of stimulus,
maybe the next version of Rails before we close out?
Any sort of planned convergence?
I know that Rails does a lot of generation in terms of scaffolding.
Any plans of stimulus being baked in and HTML going down the pipe
with data attributes?
Anything left unplugged?
What is coming is greater integration with Webpack.
So we already have wonderful integration with Webpack
through a gem called Webpacker,
which gives you a way of using Webpack
in a Rails application in an easy way.
And Stimulus is made for that.
Stimulus is made for use with Webpack.
You can use it outside of that.
There is a compiled version.
You can do that.
But most people, most of the time, will use it with Webpack or something similar.
And that's the direction we're moving towards.
We're moving away from generating or compiling JavaScript through the asset pipeline.
The asset pipeline goes all the way back to, I think, 2008 or something, where we had to build our own tooling for compiling JavaScript because there just wasn't a good ecosystem around JavaScript itself to do it.
Well, that's totally different now, right?
There's actually multiple competing ways of compiling JavaScript, and many of them are very fine, and we've decided to pick Webpack as the default.
And we should take fuller advantage of that
because that's where most of the development is happening.
So the asset pipeline can kind of step back now
that the job of compiling JavaScript is so eminently handled
by the JavaScript community itself and say,
you know what, I'm just going to hand this responsibility over.
So Rails 6, as we're coming up upon after the
real release of rails 5.2 is going to figure out a way to make that blend beautifully and have an
emphasis and a focus on webpack out of the box which then also means a focus on making it super
duper easy to use stimulus out of the box because you'll basically just be able to say Rails new my app dash dash webpack equals stimulus
and we'll set you up with a default scaffold
for using stimulus.
Even though there's not even that much scaffolding to set up
but I find oftentimes what keeps people back
and stop them from using something
is just getting hello world.
Just getting all the things wired up
and I think the joke,
particularly in the JavaScript community,
is that you have to spend two days setting up
your compilation pipeline before
you even get to Hello World.
So Rails certainly wants to
assist you with that, so that you can just run
one command and you can see Hello World is already there
and then you can start filling in with your own application
code. So that's one area that we're focused
on. And just mending relations, perhaps, in some ways
with the JavaScript community.
I think I've certainly had a contentious relationship
with JavaScript for many years
because I thought ES3 was a kind of shitty language,
to be quite frank,
for this kind of style, object-oriented focus
that I wanted to write
and that the ecosystem around interfacing
with the dom and the differences between browsers just made it a miserable experience well um
lo and behold 10 years makes such a difference and things are different now and and i've come
to really enjoy javascript in in many of its forms and stimulus is certainly one of those forms so
i'm just really excited to share that
with the world and share a different paradigm as we've talked about from the json over the wire is
the only way to go idea to say like hey do you know what html is actually a wonderful wire format
and and we've used it successfully for what 30 years now and it still has legs in it and then
some and it has clear advantages on productivity and other things.
So let me promote that as an alternative path.
I have no illusions that just because we promote this alternative path
that everyone will switch to it.
I think there's tons of momentum, and deservedly so,
around solution like React and elsewhere.
We don't have to win total domination.
I think development communities sometimes have this mistaken notion that it's all about market share and it's all about picking a winner.
To me, there's just such wonderful beauty in the diversity of the web. any kind of implementation language that we wanted on the back end, that you could write it in Perl, Ruby, or AppleScript,
and as long as it output HTML, the user's none the wiser,
which just gave a rise to just such a wonderful diversity
that we could have people of all different inclinations and brain shapes
find exactly the language and the environment that spoke to them.
And I hope that everyone has the opportunity to find a language and an
environment that speaks to them as much as Ruby and Rails has spoken to me.
And I think that we can carry that into the JavaScript land in large extent
now because of the advances with transpilers and so forth.
And so should we with the proliferation of different paradigms.
Well, David, thank you so much for your time today, man. I appreciate, you know,
your willingness to experiment, to dive into Basecamp and extract patterns and come out with
what is not stimulus. And then, you know, your passion for open source to release it because
you can and because you have gratitude back towards the tools that you've used. And
we appreciate that about you. So thank you very much. Thank you. I can honestly say that
it's entirely my pleasure. All right, that's it for this episode of the change log. If you enjoy
the show, do us a favor, go on Apple podcasts, rate the show, tell your friends, tweet about it,
whatever you got to do, share with a friend because this show is awesome and more people need to listen to it
a huge thanks to our sponsors rollbar linode and our new sponsor glyphe and bandwidth for changelog
is provided by fastly so head to fastly.com to learn more. Aaron Monterings by Rollbar. Rollbar.com. And everything we do is hosted on Linode servers.
Head to Linode.com slash changelog.
Check them out.
Support this show.
This show is hosted by myself, Adam Stachowiak, and Jaren Santo.
Editing is by Jonathan Youngblood.
Music is by Breakmaster Cylinder.
And you can find more shows just like this at changelog.com.
And also subscribe to our weekly email.
Thanks for listening. Thank you.