RailsConf 2019 - The Selfish Programmer by Justin Searls

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
(energetic music) - Okay, so the title of this presentation is The Selfish Programmer and it is an exploration of what you can learn by writing software for yourself, as opposed to on a team or part of a larger organization. Now, my research shows that the three most important traits in the successful solo programmer are that they be antisocial, egotistical, and irresponsible. And we're gonna spend time today discussing all three. Now, you may know me by this old picture that doesn't quite look like me or as @searls on Twitter and GitHub and I am a self-professed expert in selfish programming. I come from a company, Test Double, we're a consulting company, our double agents join client teams as additional developers to work alongside them and get things done, while also searching for ways to help the whole team improve over time. Now, I'm here today 'cause I have a problem and that is that learning Japanese is really hard. I've been working on it for 15 years and I still have a long way to go. But a couple years ago, I found an application called WaniKani and it helps you memorize kanji and vocabulary, it uses a spaced repetition system or SRS to time when you should review items to help you memorize them. So it'll challenge you to remember something for a day and see if you can remember it for three days and if you can, then maybe a week, but if you get one wrong, you'll just review it more soon and as time goes on it might be a month before you see an item. And then the app assumes that if you can remember something for six months, then you probably know it, and it'll consider that one done and you can focus on the remaining 8,000 words. The interface, of course, is like a little flash card game. So you see Japanese and then you provide a reading. So, (speaks in foreign language) in this case. As well as it'll challenge you to provide the English meaning and this word means tug of war. And because I got it right, the timer system's gonna push it off to a later review date. So what I found was that this application was awesome for teaching me how to recognize Japanese and understand it in English. It was very good for that, I can read a lot better now than I used to, but for being able to produce Japanese words out of English ideas, it just wasn't very effective, because it doesn't practice that muscle. So when I was talking to my conversation partner, I'd often be tongue-tied trying to think of the right word, so I built an application called KameSame, which is a companion app to WaniKani and is literally the same thing, except in reverse. So you see an English prompt and then you use a Japanese keyboard to provide the word and if you get it right or wrong it uses the same kinda timer system for helping you memorize how to produce the word. Additionally, it was important to me to make it a progressive web application, so that it could survive disconnects, as well as it's a great way to practice the kana flick keyboard that's popular in Japan. And most importantly, 'cause this is all about learning, building KameSame taught me a lot about selfish programming, and so today, I'd like to start talking about why it can be good to be a little bit antisocial. So, world one, stage one, the selfish programmer is unambitious and because it's easier to stay motivated when your goals are incremental and achievable. When goals are too ambitious, we run the risk of exhausting ourselves without accomplishing anything, which might lead to us quitting and giving up. 'Cause work, work is different. At work, our apps are big, they have lots of different parts, they have their own operations, and those have their own parts. And we can trust that if somebody is working on this part of the app over here, we can safely focus in this area over here, knowing that they've got our backs covered. But the problem with large apps is that our brains aren't big enough to fit them all. If we try to quickly move from area to area, the amount of context which it can cost is sort of like memory paging and it's just wasteful and inefficient. So people tend, over time, to specialize in large systems on one area, even if they run the risk of kinda forgetting how to build an entire app all by themselves. And just talking about this problem makes me nostalgic for 2005, when I first started using Ruby and Rails, 'cause on my mind, what made Ruby famous was that it enabled developers as individuals to make small, useful apps without any help. And because Ruby makes it easier to keep the whole app in your head at once, it's also great for moving more quickly throughout a code base. So all that nostalgia was making me excited to create a new, solo Ruby app for the first time in a few years, but even after several days of effort, I unfortunately only completed one small part of this flashcard app that I wanted to build and it was useless by itself. And it got me thinking about all the other pieces that I'd have to build and it was really demotivating. Somehow, over 14 years, I'd un-learned how to make small things, I'd become too ambitious. And so I instituted what I call the one weekend rule, where, if I'm not able to release a project by Sunday night, I don't do it. And the one weekend rule is a liberating constraint, 'cause it's forced me to shrink down my dreams to something small enough that I can get done, with the side effect of also fitting inside my brain, so I'm able to work more quickly as I do. So I tried to imagine the tiniest useful thing I could make and I decided on a gem to tell me whether my flashcards were ready for review or not in WaniKani. It's called $whenkani and when you run it, it'll tell you, "Oh, you know, maybe you don't have "flashcards now, come back in 11 hours." And when I do have flashcards, it'll print the URL that I should go to to go study. Really simple code, of course, it just uses net/http and json and implements a single method, says, like, "Hey, how many seconds to your next review?" So it goes out to a URL, parses the response, if there's no error then it reads the information. If I've got reviews available, of course, the answer is zero seconds, but otherwise, it'll do some math. And this was super simple, it was easy to test and it was easy to fit in my head. And importantly, it gave me experience working with WaniKani's API, so this helped solve one part of the larger application that I wanted to build and would make it easier for me to build that later. So, excited, for my next weekend project, I decided I wanted to take a bite of the apple and build the English to Japanese flashcard game. But that was just too much to do at once, 'cause it involved complex game logic as well as a complicated user interface. I'd never finish that in one weekend. So I carved off just a small piece, just the game logic, and now to solve the problem of how to make that useful so I could iterate, I decided to wrap it in a throwaway command line app. So that app looked like this and I spent several weeks just working on this, I'd get an answer right and wrong and change what got printed out. Realize that there were a lot of synonyms, and so I had to handle that gracefully. I did things like persist my progress over time and set up all those timers. And because I knew this was a throwaway CLI, it was intentionally trivial, it was just a while loop with read line and a bunch of put statements and when you strip away those bits, what you're left with is actually interesting. I had code that actually could create real review cues that could accurately judge the responses that I was giving it and that could persist my progress, so that weekend project was actually a huge success, because it became the actual code that still runs the real web application today. So incremental accomplishment can be really motivating and that's why the selfish programmer is unambitious and works to shrink their dreams down until they're easy to accomplish. All right. Let's talk antisocial stage two. The selfish programmer is ungrateful of Open Source, and understands that if dependencies are added carelessly, they may create an unmaintainable mess for themselves. So I like to envision applications like pyramids. At the top is our actual application code and below are all of our dependencies and at work, we're used to really large applications and so the marginal cost of adding just one more gem seems pretty low. But when you're solo you know that your time is limited. And so if we add too many gems, things like upgrades and workarounds may eventually consume more time than we have to give the app. But arbitrarily limiting ourselves to just two or three gems is not much of a solution, right, because then we wouldn't be able to build very useful things. And so this is why I started to categorize my dependencies based on how much I trust them and their maintainers to take care of stuff for me. For example, even though Rails is very large and installs 41 other gems, I trust Rails core to carefully curate those things and make it easy for me to stay up to date. And so, I conceive of my jump file as sort of two halves, there's smaller gems maybe where I don't know the maintainer or they're not very well-maintained and if something goes wrong, I'm probably going to be on the hook for taking care of it. And then there's the really popular gems where certainly my little tiny app isn't going to be the first one to discover a problem and somebody else is probably going to write a patch for me and I worry less about those ones. Separately, at work, I've been conditioned to never reinvent the wheel, that is, write my own code if there's already other code that could do it for me. But when you're solo, you can choose to be ungrateful, and write some code, even if there's a gem that claims to do the same thing. So, for example, maybe a gem works, but it fetches the whole universe just for one feature, and the gem's usefulness isn't worth the maintenance cost of 28 additional gems. Another problem is when a gem is too hard to learn, getting frustrated by a framework or a library is one of the top reasons that people quit their solo projects. And, you know, reinventing the wheel might be bad, but outright quitting is certainly worse. So, for example, talking about specialization, at work, I've never really been responsible for authentication features, but for my solo app I was by myself and somebody had to figure it out. And in the past I've tried to use the popular gem, Devise, but I've failed miserably each time at understanding it, and so I was afraid that if I tried it again, I'd get frustrated and I'd quit the whole project. So I started with a super basic password field, installed bcrypt, and I used Rails' has_secure_password, and it actually worked, that gentle climb was all I needed for the first five months of the app's existence. And yes, I eventually added custom features like changing passwords and verifying emails and, you know, a forgot password reset link. And yes, those customer features were probably complex enough that I would have spent, you know, more time writing them than just learning Devise in the first place. And yes, I'll admit that generating custom tokens and saving them and emailing them and then handling the little click and the verification emails, it all felt silly because I knew that Devise could do this for me, but I'm still proud that I did it by myself, because I understand my implementation completely. And KameSame gave me a safe place to practice something that I wasn't very comfortable with. So now I don't feel so stupid when I'm talking about authentication anymore. So you can try to solve every single problem that you face by Googling for gems to do it for you. But just because dependencies are easy to install doesn't mean that they're going to be easy to deal with later. So this is why the selfish programmer is ungrateful. You know, whether at work or solo, it pays off to think critically about the trade offs that we face for each dependency that we add. All right, antisocial stage three. The selfish programmer is ungenerous and doesn't worry too much about making code reusable. See, at work I was trained to share as much code as possible in order to be a good teammate, and so whenever I added some code I could, of course, could have put it to live with the feature that I was writing, but more often I put it, you know, in a place where others would find it, like in a model and then I invoked it for my feature. And this is fine, but eventually when teams work this way, it can really slow them down, for example, maybe a second feature also adds some code to that model and calls it, a third feature maybe reuses a bit of code and a fourth feature reuses some more. And after this if one of these other callers needs to make a change to that shared code, you have to consider all the other things that call it, because it might blow them up, they might break their behavior. And so it requires a degree of caution where you have to check every single call site for any bit of shared code. Additionally, when you have these kind of like high-traffic, high-churn areas like models and Rails applications, it run the risk of drying up internal private methods and where they call each other and pretty quickly you can end up in this tangle where everything calls everything. And so making any change can be really precarious and difficult. So instead I prefer to soundproof my code, and that means I put it in, instead of shared places I try to have the code live with the feature and I only extract it if it proves to be valuable later. So as a result, most of my code is pretty isolated and I can safely and aggressively change and refactor it, and most of my models and things are just dumb value types that I pass in and out of those features. This impacts how I divide responsibilities when I'm coding, for example, this controller action does two things. First, it invokes feature code to perform a search and second, it invokes general utility code that formats the results for the API. I split these responsibilities up to soundproof the feature code and minimize the general utility code. So the feature specific code is only called one time, which means I can change it really aggressively, and we can be as messy as I needed to be. But the general purpose code is kept really minimal, because it's called in seven different places and so it's much more work if I want to change it. Separately I don't allow word, the word model or any active record model, to have much, if any feature logic in itself. I treat Rail subclasses as a DSL for configuring rails and not a place to put my own custom code. Because if I were to add just one method to my word.rb file, it would look small, but it would actually be its 312th method. And that means the contract between it and the people who call it is kind of murky. But if I create a separate class with just one purpose and one method, it's going to have a clear contract with anyone who calls it, and I've got evidence, soundproofing code really works, because for over one year KameSame started as actually, this code base was just an example sentence search engine that was completely unrelated called SenTensei, and only later did I add KameSame right on top of the same models and database and it required zero changes to SenTensei for them both to work, and best of all, a year later I deleted SenTensei and KameSame was none the wiser, everything just worked, the code was separated. So at work code is often treated, especially by management as a valuable asset, and that gives us this mindset of thinking about like reuse will somehow make our teams go faster, but the more places that call a function, the more careful our changes have to be and that's why the selfish programmer is ungenerous and not afraid to write a little extra code in order to gain the flexibility of changing it aggressively later. So that's a little bit about why it can be nice to be antisocial when you're programming by yourself. So now let's talk about the virtues of being egotistical. So world two, stage one. The selfish programmer is delusional, because sometimes it's necessary to believe that your code is good enough to ship, even when it's really bad. (audience laughing) So this is the KameSame homepage, you'll see that you have progress here like XP and level and that sort of thing. And it's driven by this simple little API. It's a small response, but look at how many queries it does, it's kind of convoluted and, this at work, this code never would have passed a code review, because once merged the team collectively owns that code. So the, you know, any problem becomes the team's whole problem and this results in our teams generally having a higher bar for quality up front. But if you never create a new branch, then you don't need to worry about pull_request reviews. (audience laughing) So... (audience laughing) because this is a solo project, I'm just pushing to master all the time and I just try to keep it working. So, the selfish programmer is delusional, because they choose to believe that features are ready, as soon as they work. And if we try to perfect that code in advance, we'd only be guessing as to what the ideal design and optimization should be. Instead we push this messy but working code, we can take time and observe errors and bug reports and feedback and gradually improve that code's behavior as we learn. And so over time I found myself adding to this route and subtracting things and adding code, but all the time resisting the temptation to optimize its performance. Instead, I waited some more. And after a long time passed without changes I could be confident that the feature's behavior was always mostly correct, 'cause code never tells us when it's done. So I just choose to believe that once code no longer needs to be changed, then it must be right. And that means then we can turn our attention to how we might optimize it. So recall, this is a lot of queries. And I needed some way to gather all that data without making so many trips to the database, because if you were to think about this architecturally, we have a lot of Ruby logic up top and that's great for prototyping, it's really easy to change, but it's super slow, right, because calling to Postgres so many times. So I spent a few hours and I reimplemented the entire feature as four SQL views, and Postgres is great at doing math and aggregation so these SQL views do everything that the Ruby was doing, except faster. So, because that behavior is finished, it's okay that this is kind of uglier and harder to change. And fun fact, actually, Active Record can talk to SQL views as if they were models just overwrite read_only to true to be safe, and you can query it just like you would any other model in your system. So, now for the payoff, we're able to go from all of this code here, to just a single actor record model that was backed by a SQL view. So the Ruby is now responsible for much less, and the code is also much simpler. And you can even look at the architecture, it looks simpler right, just a little bit of Ruby calling to an admittedly kind of complex Postgres query. And predictably that route is now much faster than it was before. And so to recap, I recommend you know we start by shipping code as soon as we can make it work. Let those changes simmer until we've made it right. And only then make it fast by pushing that code down to, you know, a faster layer. So peer pressure to write good code at work has kind of made me a perfectionist over the years, but this results in the future-proofing behavior and optimization, before I have any real data to base those decisions on. So that's why the selfish programmer is a little bit delusional, believing that messy code is good enough to put in front of users, so that they don't waste time guessing how to perfect it. Next up, egotistical stage two, the selfish programmer is narcissistic because they ask only how an activity will benefit them before they decide whether to do it. So, at work our large apps usually have lots of units and lots of tests of those units that call them and then assert a response. We also have full stack tests that interact with the whole system and then verify it works, and on a team, these different types of tests, address very different needs, full stack tests, for example, help build trust that our app is doing what it's supposed to do. And when we have lots of things, tests create a safety net so we can focus on one area with being sure that we're not breaking other things. And third, tests can help express our intentions to our teammates and to our future selves, of what we're really trying to accomplish with our code. But these testing benefits have less value on solo projects, like you're the one determining the direction so you don't need to prove to anybody what you're doing. The app is probably small enough that you can focus and move quickly. And when you're by yourself there's probably more efficient ways to denote your intentions to yourself than writing a whole bunch of tests. So because of this I took a new approach to testing for KameSame, it's called AI testing, and I'd like to demo it for you now. So here's a sped-up recording of a full test run, single test is going to wind through a bunch of different parts of the application. You've seen browser testing before, it looks a lot like that, doing things like doing some flashcards, searching. Going to the account settings, change your name, change your password, logout, login, that kind of stuff, and this is just one test, but it actually gets pretty good code coverage, it's like 80% coverage, so I'm relatively confident that when this test passes, the app probably works, and I go ahead and push to master. The downside though is that this test three whole minutes of my build to run. And I want to be able to push the master and go as fast as I can so that I can go about my day as quickly as possible. So this is where AI testing really shines. First, it looks at only the most recent changes to the code. And then it only runs the unit tests that were affected by that change set. And then finally, the browser based test dynamically adjusts to only cover the affected area. So here's the same test run again, after I make a change just to one feature. It's gonna do some flashcards, but then it's going to hone in on the custom spelling feature, it's going to spend extra time there, go back, perform a search, verify that it's there, and so on and so forth. And of course, the coverage is much much lower for this test run, but the confidence is still high, because the test focused on where the code had actually changed. And best of all, I got the build down to just 20 seconds. And so I should confess, however, that AI testing stands for average intelligence. And this was just me recording videos with simple code enabled. Truth be told, I actually didn't write any automated test for KameSame, (audience laughing) 'cause I wouldn't have gotten a good return on my investment. Remember, the primary remaining benefit was it telling me whether the app worked, but I intentionally designed the app to be really easy to manually check whether it was working by being focused and narrow. Because on teams good testing tends to pay for itself right away, you know, yes, you invest in writing them and running them and changing them. But you know, you get to run them locally, you run them in CI, maybe your teammate makes changes and then runs them in CI and by the time that you've merged them into master, you have probably gotten a payoff, you know, more benefit than you've invested in good tests. But when you're solo, that investment cost is similar, you still have to build and run and change them all. But the value is mostly limited to running them locally. And then once MCI and yeah, they might pay off eventually. But at least initially, there's a little bit of a gap there. You see, most activities the developers do to deal with software problems can be categorized as either trying to maximize MTTF, or mean time to failure, that stuff that we do to prevent things from breaking, or minimize MTTR, the mean time to repair, the amount of time before we fix something when it does break. It's probably important for us as professionals to balance our skills between these two types of remediation. So personally, I've spent most of my career mostly focused on maximizing MTTF, I rely on tests and compilers and linters to avoid production issues outright. But by not writing tests, KameSame has given me a great chance to improve my skills at monitoring servers and analyzing error reports and debugging problems. So a lot of people assume that things like testing are always worthwhile just because somebody told them to. But when you're solo, it's your time that you're investing. So you need to be more critical like, this is why the selfish programmer is narcissistic. It was only after deciding that I was spending my time to benefit myself that I could do the math and then stop feeling guilty for the fact that I wasn't writing tests for my own solo projects. Egotistical stage three. The selfish programmer is domineering, because they're comfortable saying no to people a lot. Most people agree that great software requires a clear, narrow vision. But telling people no is really hard. When you're solo, you're not just the only developer and the only tester, you're the only product owner, too, and product owner is a really hard job. At work, product owners have to juggle the concerns of many different stakeholders, marketing, finance, security, support, not to mention the needs of the application itself. And product owner has to set priority which is going to make some people happy and frustrate others. So I decided early on, I was always going to release KameSame for free. 'Cause as of today, we have about 2,300 users, who've done millions of reviews. But as of today, KameSame still only has one customer, me. And I have zero other stakeholders. So that makes it much easier for me to tell people no, as product owner, I knew exactly the features that I wanted at first. But after launch, I kind of ran out of good ideas. I just needed some inspiration. And so I started a thread at WaniKani's forum. And it's got a whole bunch of posts. Basically, this is me just farming ideas out of this community of how I can improve the application. So I give KameSame away for free, and I get free ideas back in return. And of course, if you read that thread, you'll see me saying no to people a lot. Because if a feature doesn't appeal to me as KameSame's only customer, I don't want to spend my personal time building it. But sometimes I get a message like this one. It was from a colorblind person who said that the color coding of kanji and vocabulary was too subtle and resulted in accidental mistakes. But I'm not colorblind. So I wasn't sure that fixing this would benefit me somehow. So I looked closer at it. And for context, here's a vocabulary card, it's asking for a multi-character adjective. And here's a kanji card asking for a single character response. Now if you desaturate them and then overlay you can see how just visually similar these two things are. So I just looked closer and realized I'm already doing a lot of client-side validation here. I'm checking for empty answers and alphanumeric answers, phonetic answers to kanji questions. I realized I could fix this by just adding one more condition. So, checking to make sure that the length is one if I'm asking for a single character and then updating this message. So now the app catches these kinds of mistakes. So if I type in that adjective, it will correct me and I can backspace and enter without the erroneous failure being recorded. So even though that feature's inspiration was somebody else's problem, the improved validation has helped me many times too, 'cause I tend to move too quickly through the app. In fact, most of my favorite features of the app were somebody else's idea solving somebody else's problem, and I benefited from it too. And so you know, at work, it can be easy to just shut up and blindly follow orders. But the selfish programmer gets lots of practice being domineering, because they must control their own priority and direction. So yeah, at work it can be uncomfortable to challenge a requirement that might be handed down to you. But I found that teams that struggle over direction together tend to write better software. So, it's not enough to be antisocial and egotistical, the selfish programmer must be irresponsible too. And I mean that literally, like I don't want to be responsible for anything. So, world three, stage one, the selfish programmer is untethered from operational responsibilities. Their apps should run themselves so that they can go and focus on they want to be doing. Now most apps, they end up with lots of operational work that has little to do with the application itself, stuff like security, performance, upgrades, and monitoring. And originally the term DevOps meant developers responsible for their own apps' operations. And the idea was, well, like we saw with automated testing, if developers own operations, they'll find ways to simplify and automate it, so that they can go back to focusing on development, but it hasn't really worked out that way. Instead, DevOps has been co-opted by all the large cloud service providers and they market increasingly complex customizable services. And this is created so much new work that now companies are hiring full time DevOps people just like they would sysadmins. And so because the definition of DevOps has changed, I'm now a no-ops advocate, I want to focus on programming, so my goal is zero routine operations work. So to reduce my apps operational costs, my first step was to minimize dependencies, follow conventions, try to reduce the number of runtime servers that I would need, but honestly, this only helped a little bit. What I really wanted was for somebody just to take care of ops for me, I wanted to be able to walk into a store, and buy some ops off the shelf. (audience laughing) Fortunately, that store exists and it's called Heroku, not, not, yeah, Heroku. (audience cheering) Not only is Heroku still good, it's better than ever, and by helping me be irresponsible of operations work, Heroku really captures the original spirit of DevOps. So this tiny file is all I need to tell it how to run my app server and that I want to run my migrations on every push. And I only pay $7 to keep the app up and $9 for all these automatic add-ons, like Heroku Postgres and Bugsnag and SendGrid, but easily my favorite Heroku features are deploy pipelines and review apps. You can see 'em at work here. So I'm going to go into Git and change a background from white to purple, commit it to a branch, I do all my coding in the GitHub web interface. (audience laughing) And as soon as I open that pull_request, Heroku's provisioning a one-off application against that PR branch, I'll click in and I'll see my purple background and this is such an awesome way to work. You know you don't have a staging environment or anything like that anymore, you just get to see somebody's PR in action, play with it and color your feedback, it's really awesome. And just set up review apps, you just create a simple little app.json file, you tell it what build pack you're using, what add-ons you need, and any script that you need to run after the fact. And my script is simple. It's just run my migrations, download some sample data, and then create a user so I can go in and test. And that's it, that's really awesome. I've seen companies invest millions of dollars into DevOps and fail to match the developer experience of a $7 Heroku Dyno, it's really cool. But most companies are going to resist no-ops and platforms like Heroku, because it's really hard to admit that they're not special, unique snowflakes. And it's true that no two apps are the same. But you know, Rails taught us that apps actually share a lot of common problems. And that's true of operations too, things like hosting, deployment, security, so why not outsource that to somebody who specializes in it? So that's why the selfish programmer is untethered by operations. They know that they're going to go further faster by focusing on what they really want to do, write software. All right, irresponsible stage two. The selfish programmer is fickle. And because solo work is a creative outlet, their opinions are going to frequently change. So at work, everyone brings their perspective and their coding style, and on some teams, everyone just sort of codes their favorite way and that might make people happy at first, but inconsistency can lead to confusion and conflict later, which would slow the team down. Ultimately, some things, even things we care a lot about just don't matter that much. And code formatting is one example, if the Ruby interpreter doesn't care, why should we? So that's why some teams try to normalize on just one style for their code, they start by choosing a way to do things and everyone does their best to conform to it. And then they hope that the code is going to be more consistent and hopefully make it easier for them to own and maintain long term. But one of the joys of solo coding is that your way is the only way, you're finally free to code however you want. But I've found that when I'm solo I'm also really fickle. My favorite way to code changes all the time. And so whenever my style changes, my code becomes inconsistent and over time and across repositories, it makes it really hard for me to maintain my own projects. So that's why linters and formatters exist, like Rubocop, that enforce consistency. But the problem is that, for each individual project, you know, we'll eventually create a large, custom style configuration and time spent arguing coming over your linter config is just wasteful bike-shedding. And since every project is likely to have a different configuration, you're just gonna waste precious brainpower keeping multiple styles in your head once. So that's why at Test Double, we created the gem Standard, it wraps Rubocop with a single configuration that is locked, and you're not able to change it, and so it can't be customized. It's made the CLI real simple. You just run Standard to see your failures and Standard Fix to fix them, so trust me, this is not Justin Searles style Standard is actually the result of many compromises. This is how I actually prefer to write Ruby code. But the first thing Standard does is forces me to use colon syntax instead of hashrockets, trailing commas at the end of array and hash literals, putting space inside my blocks and then using leading dots instead of trailing dots on method chains. So now I write Ruby like this and I don't love it, but I value the greater consistency. So if you adopt Standard, our hope is that you'll maintain consistency without worrying so much or arguing about every little rule. And best of all, if you don't like something, you can blame the tool or me, instead of one another. (audience laughing) So the selfish programmer is fickle and opinions about unimportant topics change over time. So sometimes it's better just to rely on a tool to help you stay focused on the work. All right, the final stage, the selfish programmer is unhelpful, they'd rather solve problems once and for all, than spend time helping others with the same issue over and over again, see, at work developers are often too far away to help users. Customer support might sit between them, and you know, supports the first responder to any kind of customer issue, but often, you know, they don't know how to do something, they might hand that off to developers, the developers might look at it and you know, at first glance, just kind of create a work-around and give it back and that'll be handed off to the users. But if the issue recurs, support's gonna look at that and they've probably been trained that developer time is very expensive and so the most likely thing is they're just gonna repeat the work-around. And I've in the past seen this happen where developers by being totally insulated from user experiences will let very simple and easy-to-fix bugs just fester for months, and even years, because support has sort of just kind of created enough duct tape and rubber bands to keep everything working for customers. So that leads to problems never really getting fixed. But if you're solo, you're exposed everything, every email, every tweet, every error, and because I hate these notifications, I'm willing to spend lots of time to make them stop. For example, I recently had a time bug, not caused by time zones or data types, but by the actual passage of time. You see, I had a controller and it eventually grew to have two different purposes. So I split it up into two new controllers and then blew away the first one. Now, the moment that I deleted that file, even though nothing else called it anymore, I suddenly got a ton of bug reports and what I learned was that some people never refresh their browser tabs. So they'd load the page, I change the server, then their JavaScript would keep calling it and, you know, create bugs. I can't believe this, but I was, (audience laughing) I was still receiving this exact bug report over six weeks later. (audience laughing) And so I didn't want to get emails like this ever again. And so I needed some way to update the client first and then leave plenty of time for that JavaScript to propagate before I change the server in a breaking way, But for a solo project, how should I do that, you know, I could write a little to-do comment for myself, like, "Delete this later." But, of course, that one was because of LeBlanc's Law, which states that later equals never. So if I, yeah, I'm just going to forget about that comment and then I'm going to have all this dead code sittin' around on my system. So I spent a couple hours, and I wrote a little gem called todo_or_die. It's a reference to an NES game of the same name. And it's a single method, which just takes a comment and a due date, so I planned to delete this code by June 2019 and as time passes, when June comes, if I run my server, todo_or_die is gonna blow up, it's going to tell me where to change my code. And in production, of course, I don't want to impact users, so todo_or_die will just log a warning instead. And now, even though, you know, that gem took a little while to create, since I started using it, I've never had a bug of the same category recur. So that's why the selfish programmer is unhelpful, because they understand that if nobody's bothering them, it must mean they did a good job. So that's just a little bit about how the selfish programmer is antisocial, egotistical, and irresponsible. And my hope is that something here resonated with your work, so that you can apply this selfishness to your own practice and craft. If you have any comments or questions please tweet at me, send me a DM, or an email. That's my email address, justin@testdouble or @searls on Twitter. If you want to check out the app that I was discussing today, it's KameSame.com. You can actually download 4k copies of all of the background artwork from this presentation at that URL. Again, our company's Test Double. You might have seen Marla's talk on remote work yesterday, you should definitely check out Eric Weinstein's talk on interviewing tomorrow after lunch. And you know, if you're interested in a career consulting or if your team's just looking for additional senior developers, you can find us at testdouble.com or on social things as @testdouble. And in closing, I hope this has made you consider, you know, programming a bit more selfishly and I just want to thank all of you from the bottom of my heart for for sharing some of your time today. (audience applauding) (cheerful music)
Info
Channel: Confreaks
Views: 7,611
Rating: 5 out of 5
Keywords:
Id: k5thkp4ZXSI
Channel Id: undefined
Length: 37min 37sec (2257 seconds)
Published: Mon May 20 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.