The Bike Shed

Follow The Bike Shed
Share on
Copy link to clipboard

On The Bike Shed, hosts Chris Toomey & Steph Viccari discuss their development experience and challenges at thoughtbot with Ruby, Rails, JavaScript, and whatever else is drawing their attention, admiration, or ire this week.

thoughtbot

Donate to The Bike Shed


    • May 27, 2025 LATEST EPISODE
    • every other week NEW EPISODES
    • 39m AVG DURATION
    • 465 EPISODES

    4.9 from 121 ratings Listeners of The Bike Shed that love the show mention: thoughtbot, ruby, rails, fixed, developers, technical, code, programming, development, always interesting, great to hear, i've learned, balance, dive, learn something, discussion, deep, practical, highly recommend, topics.


    Ivy Insights

    The Bike Shed podcast, a side project of Thoughtbot, has exceeded my expectations in terms of production quality and content. With an array of interesting guests and topics, this podcast keeps me engaged and entertained. I particularly appreciate that the hosts source many of their guests internally, as it creates a comfortable environment for deeper discussions. Another standout feature is the fact that they cover various programming ecosystems, not just focusing on one language or framework. This unique approach makes The Bike Shed podcast both informative and enjoyable.

    One of the best aspects of The Bike Shed podcast is the delightful cadence of conversations between hosts Chris and Steph. They manage to provide in-depth insights without overly explaining concepts, striking a perfect balance. Furthermore, the practical issues discussed are relatable and offer valuable solutions for everyday development problems. The diversity of tech thinkers featured on different episodes is both inspirational and educational.

    While there aren't many negative aspects to highlight about this podcast, some listeners may prefer more focused discussions on specific languages or frameworks. Although The Bike Shed covers various ecosystems, the majority of content revolves around Ruby on Rails development. However, considering that the hosts are experienced Rails developers themselves (one even being part of the Rails core team), this specialization can be seen as an advantage rather than a drawback.

    In conclusion, The Bike Shed podcast by Thoughtbot is a unique and enjoyable programming podcast that offers valuable insights into web development and Ruby on Rails development in particular. With its well-produced episodes featuring interesting guests from the tech community, this podcast stands out among others in its genre. Whether you're seeking inspiration or practical solutions to everyday development problems, The Bike Shed delivers both in an engaging manner. I highly recommend giving it a listen!



    Search for episodes from The Bike Shed with a specific topic:

    Latest episodes from The Bike Shed

    465: What is quality software with Elaina Natario

    Play Episode Listen Later May 27, 2025 37:52


    Elaina Natario returns to talk with Joël about what makes good quality product design and the priorities that shape development. The pair discuss the importance of certain elements such as security and accessibility, maintaining certain standards throughout development, as well as judging the practical applications of prototypes within a project and the broad role they play. — The Sponsor for this episode has been Judoscale - Autoscale the Right Way (https://judoscale.com/bikeshed). Check out the link for your free gift! You can read more about about inaccessable prototypes here (https://localghost.dev/blog/ai-and-the-trouble-with-inaccessible-saas/), or listen to the episode Joël mentioned with Aji about different typescripts here (https://bikeshed.thoughtbot.com/458)! Your guest for this week has been Elaina Natario (https://www.linkedin.com/in/elainanatario/) and you host has been Joël Quenneville (https://www.linkedin.com/in/joel-quenneville-96b18b58/). If you would like to support the show, head over to our GitHub page (https://github.com/sponsors/thoughtbot), or check out our website (https://bikeshed.thoughtbot.com). Got a question or comment about the show? Why not write to our hosts: hosts@bikeshed.fm This has been a thoughtbot (https://thoughtbot.com/) podcast. Stay up to date by following us on social media - YouTube (https://www.youtube.com/@thoughtbot/streams) - LinkedIn (https://www.linkedin.com/company/150727/) - Mastodon (https://thoughtbot.social/@thoughtbot) - BlueSky (https://bsky.app/profile/thoughtbot.com) © 2025 thoughtbot, inc. — Credit: Ad-read music by joystock.org

    464: Modelling the stars with Rémy Hannequin

    Play Episode Listen Later May 20, 2025 42:59


    Joël and Rémy draw inspiration from the stars as they discuss Rémy's new open source Ruby gem, Astonoby (https://github.com/rhannequin/astronoby). Rémy reveals the challenges he faced in taking on this project, the scientific translation work that went into making it accessible for everyone, as well as the key lessons he learnt from modelling the cosmos. — The Sponsor for this episode has been Judoscale - Autoscale the Right Way (https://judoscale.com/bikeshed). Check out the link for your free gift! If you're enthusiastic about space and want to try out Rémy's new gem tool, you can find it here (https://github.com/rhannequin/astronoby). Alternatively you can read more about astronomical computing here (https://dev.to/rhannequin/series/17782). Your host for this episode has been thoughtbot's own Joël Quenneville (https://www.linkedin.com/in/joel-quenneville-96b18b58/) and was accompanied by Rémy, who can be found over on LinkedIn (https://www.linkedin.com/in/rhannequin/?locale=en_US), or through social media (https://mastodon.social/@rhannequin@ruby.social) under the handle @rhannequin If you would like to support the show, head over to our GitHub page (https://github.com/sponsors/thoughtbot), or check out our website (https://bikeshed.thoughtbot.com). Got a question or comment about the show? Why not write to our hosts: hosts@bikeshed.fm This has been a thoughtbot (https://thoughtbot.com/) podcast. Stay up to date by following us on social media - YouTube (https://www.youtube.com/@thoughtbot/streams) - LinkedIn (https://www.linkedin.com/company/150727/) - Mastodon (https://thoughtbot.social/@thoughtbot) - BlueSky (https://bsky.app/profile/thoughtbot.com) © 2025 thoughtbot, inc. — Credit: Ad-read music by joystock.org

    463: All about modals with Elaina Natario

    Play Episode Listen Later May 13, 2025 36:42


    Joël strikes up a dialogue with fellow thoughtboter Elaina Natario about the various use cases of modals. Together they discuss their pros and cons, the dos and don'ts, their functionality and accessibility to the end user as well as the subtle differences you'll notice when compared to dialogs. — The Sponsor for this episode has been Judoscale - Autoscale the Right Way (https://judoscale.com/bikeshed). Check out the link for your free gift! Your guest for this week has been Elaina Natario (https://www.linkedin.com/in/elainanatario/) and you host has been Joël Quenneville (https://www.linkedin.com/in/joel-quenneville-96b18b58/). If you would like to support the show, head over to our GitHub page (https://github.com/sponsors/thoughtbot), or check out our website (https://bikeshed.thoughtbot.com). Got a question or comment about the show? Why not write to our hosts: hosts@bikeshed.fm This has been a thoughtbot (https://thoughtbot.com/) podcast. Stay up to date by following us on social media - YouTube (https://www.youtube.com/@thoughtbot/streams) - LinkedIn (https://www.linkedin.com/company/150727/) - Mastodon (https://thoughtbot.social/@thoughtbot) - BlueSky (https://bsky.app/profile/thoughtbot.com) © 2025 thoughtbot, inc. — Credit: Ad-read music by joystock.org

    462: Decomposition as a key developer skill with Steve Polito

    Play Episode Listen Later May 6, 2025 42:18


    Joël and Steve sit down to discuss the ins and outs of decomposition within their respective workflows and how they use it to their advantage when working on certain projects. Together they look at working with vertical slices over other decomposition methods, when and how to break down code as efficiently as possible, and Joël lays out his three key principles that help him write code dubbed “The Triangle of Separation”. — The Sponsor for this episode has been Judoscale - Autoscale the Right Way (https://judoscale.com/bikeshed). Check out the link for your free gift! Learn more about Joël's triangle of separation (https://thoughtbot.com/blog/triangle-of-separation) and working with vertical slices (https://thoughtbot.com/blog/break-apart-your-features-into-full-stack-slices)! Your guest this week has been Steve Polito (https://www.linkedin.com/in/steve-polito), and your host for this episode has been thoughtbot's own Joël Quenneville (https://www.linkedin.com/in/joel-quenneville-96b18b58/). You can find Steve's work over on GitHub (https://github.com/stevepolitodesign), or dive into more of his thought processes over on his thoughtbot's blogs (https://thoughtbot.com/blog/authors/steve-polito). If you would like to support the show, head over to our GitHub page (https://github.com/sponsors/thoughtbot), or check out our website (https://bikeshed.thoughtbot.com). Got a question or comment about the show? Why not write to our hosts: hosts@bikeshed.fm This has been a thoughtbot (https://thoughtbot.com/) podcast. Stay up to date by following us on social media - YouTube (https://www.youtube.com/@thoughtbot/streams) - LinkedIn (https://www.linkedin.com/company/150727/) - Mastodon (https://thoughtbot.social/@thoughtbot) - BlueSky (https://bsky.app/profile/thoughtbot.com) © 2025 thoughtbot, inc. — Credit: Ad-read music by joystock.org (https://joystock.org)

    461: Writing new vs existing code with Sara Jackson

    Play Episode Listen Later Apr 29, 2025 42:20


    Joël talks with fellow thoughtboter Sara about the different ways developers can help one another across the various stages of an app's lifecycle. They highlight the importance of utilising notes early on to clarify your work for others who may need it later down the line, how tooling can aid with this at all stages of development, and what lessons can be learnt from coding decisions made by someone else. — The Sponsor for this episode has been Judoscale - Autoscale the Right Way (https://judoscale.com/bikeshed). Check out the link for your free gift! Check out Steve Polito's Hotwire essentials project (https://thoughtbot.com/blog/hotwire-essentials-is-here-learn-hotwire-by-building-a-podcast-player), featured in Episode 452 (https://bikeshed.thoughtbot.com/452). Your host for this episode has been Joël Quenneville (https://www.linkedin.com/in/joel-quenneville-96b18b58/) and was accompanied by Sara Jackson (https://www.linkedin.com/in/saraejackson/). If you would like to support the show, head over to our GitHub page (https://github.com/sponsors/thoughtbot), or check out our website (https://bikeshed.thoughtbot.com). Got a question or comment about the show? Why not write to our hosts: hosts@bikeshed.fm This has been a thoughtbot (https://thoughtbot.com/) podcast. Stay up to date by following us on social media - YouTube (https://www.youtube.com/@thoughtbot/streams) - LinkedIn (https://www.linkedin.com/company/150727/) - Mastodon (https://thoughtbot.social/@thoughtbot) - BlueSky (https://bsky.app/profile/thoughtbot.com) © 2025 thoughtbot, inc. — Credit: Ad-read music by joystock.org (https://joystock.org)

    460: Programer Productivity with Valerie Burzynski

    Play Episode Listen Later Apr 22, 2025 36:10


    Start taking notes in this episode as Joël and Valerie discuss the different ways in which they structure their note taking systems to improve their workflows. Together they cover the best ways to get started with serious note taking, how to best map out your thoughts so they make the most sense when you come back round to them, as well as examining the different use cases they have for them both over the course of a working day. — The Sponsor for this episode has been Judoscale - Autoscale the Right Way (https://judoscale.com/bikeshed). Check out the link for your free gift! Take notes like a pro with Obsidian (https://obsidian.md/) and then read what Joël has to say on his own note taking (https://thoughtbot.com/blog/my-note-taking-system-gives-me-constant-content-ideas). Your guest this week has been Valerie Burzynski, (https://www.linkedin.com/in/valerieburzynski/) and your host for this episode has been thoughtbot's own Joël Quenneville (https://www.linkedin.com/in/joel-quenneville-96b18b58/). If you would like to support the show, head over to our GitHub page (https://github.com/sponsors/thoughtbot), or check out our website (https://bikeshed.thoughtbot.com). Got a question or comment about the show? Why not write to our hosts: hosts@bikeshed.fm This has been a thoughtbot (https://thoughtbot.com/) podcast. Stay up to date by following us on social media - YouTube (https://www.youtube.com/@thoughtbot/streams) - LinkedIn (https://www.linkedin.com/company/150727/) - Mastodon (https://thoughtbot.social/@thoughtbot) - BlueSky (https://bsky.app/profile/thoughtbot.com) © 2025 thoughtbot, inc. — Credit: Ad-read music by joystock.org

    459: Paper Data Structures with Sally Hall

    Play Episode Listen Later Apr 15, 2025 42:20


    Joël and thoughtbot colleague Sally Hall set out to find an answer to the question, what exactly are the differences between paper data structures and digitals ones? They compare the different ways humans store and access data, from rolodexs to the dewey decimal system, browsing a system vs searching it, and how the digital age has changed the way we assess and look at data stored in those systems. — Change your organisational workflow and get yourself a Rolodex! (https://www.rolodex.com/contact-management.html) Find out more about the Dewey Decimal System (https://esu.libguides.com/dewey). Your guest this week has been Sally Hall (linkedin.com/in/sallyannahall), and your host for this episode has been thoughtbot's own Joël Quenneville (https://www.linkedin.com/in/joel-quenneville-96b18b58/). If you would like to support the show, head over to our GitHub page (https://github.com/sponsors/thoughtbot), or check out our website (https://bikeshed.thoughtbot.com). Got a question or comment about the show? Why not write to our hosts: hosts@bikeshed.fm This has been a thoughtbot (https://thoughtbot.com/) podcast. Stay up to date by following us on social media - YouTube (https://www.youtube.com/@thoughtbot/streams) - LinkedIn (https://www.linkedin.com/company/150727/) - Mastodon (https://thoughtbot.social/@thoughtbot) - BlueSky (https://bsky.app/profile/thoughtbot.com) © 2025 thoughtbot, inc.

    458: Learning Typescript with Aji Slater

    Play Episode Listen Later Apr 8, 2025 42:31


    Joël and fellow thoughtboter Aji Slater examine the unfamiliar world of Typescript (https://www.typescriptlang.org/) and various ways of working within it's system. They lay out the pros and cons of Typescript over other environments such as Ruby and Elm and discuss their experience of adopting LLM partners to assist in their workflows. Utilising Chat GPT and Claude to verify code and trim down syntax, all while trying to appease the type checker. Discover the little tips, tricks and bad habits they picked up along the way while working with their LLM buddies in an effort to improve efficiency. — Check out Ruby2D (https://www.ruby2d.com) for all your 2D app needs! You can connect with Aji via LinkedIn (https://www.linkedin.com/in/doodlingdev/), or check out some of the topics he's written about over on his thoughtbot blog (https://thoughtbot.com/blog/authors/aji-slater). Your host for this episode has been Joël Quenneville (https://www.linkedin.com/in/joel-quenneville-96b18b58/). If you would like to support the show, head over to our GitHub page (https://github.com/sponsors/thoughtbot), or check out our website (https://bikeshed.thoughtbot.com). Got a question or comment about the show? Why not write to our hosts: hosts@bikeshed.fm This has been a thoughtbot (https://thoughtbot.com/) podcast. Stay up to date by following us on social media - YouTube (https://www.youtube.com/@thoughtbot/streams) - LinkedIn (https://www.linkedin.com/company/150727/) - Mastodon (https://thoughtbot.social/@thoughtbot) - Bluesky (https://bsky.app/profile/thoughtbot.com) © 2025 thoughtbot, inc.

    457: Time Zones with Sally Hall

    Play Episode Listen Later Mar 18, 2025 46:29


    Joël enlists the help of thoughtbot colleague Sally Hall as they dive into the complex world of time zones, daylight savings, measurements and coding. Together they discuss their struggles with daylight savings throwing off their recent project reporting, the constant struggles of writing for different time zones and why writing your own code is never worth the hassle, and the similar battle of writing for different units of measurement. — Check out the idea behind "If Hemingway Wrote Javascript (https://javascriptweblog.wordpress.com/2015/01/05/if-hemingway-wrote-javascript-explained/)" and how it could help you with your coding. Watch Tom Scott's (https://www.youtube.com/watch?v=-5wpm-gesOY) own slow decent into madness over timezones and coding. Your guest this week has been Sally Hall (https://www.linkedin.com/in/sallyannahall), and your host for this episode has been thoughtbot's own Joël Quenneville (https://www.linkedin.com/in/joel-quenneville-96b18b58/). If you would like to support the show, head over to our GitHub page (https://github.com/sponsors/thoughtbot), or check out our website (https://bikeshed.thoughtbot.com). Got a question or comment about the show? Why not write to our hosts: hosts@bikeshed.fm This has been a thoughtbot (https://thoughtbot.com/) podcast. Stay up to date by following us on social media - YouTube (https://www.youtube.com/@thoughtbot/streams) - LinkedIn (https://www.linkedin.com/company/150727/) - Mastodon (https://thoughtbot.social/@thoughtbot) - Bluesky (https://bsky.app/profile/thoughtbot.com) © 2025 thoughtbot, inc.

    456: Typescript with Jimmy Thigpen

    Play Episode Listen Later Feb 25, 2025 37:19


    Joël turns to fellow thoughtboter Jimmy Thigpen as he looks to expand his knowledge about the wide world of Typescripts. Together they discuss the differences between Typescript and other common systems such as Elm and Javascript, how to best handle their edge cases and error flags, as well as the benefits of using Zod as your typescript library. — Just starting out in Typescript? Try enabling Strict Mode! (https://www.typescriptlang.org/tsconfig/#strict) Try out Zod for yourself (https://zod-playground.vercel.app/) in their browser playground, or check out Zod's homepage (https://zod.dev/) for more info. If you'd like to contact Jimmy about all things Typescript he can be found over on LinkedIn (https://www.linkedin.com/in/thigpenjimmy/) Your host for this episode has been thoughtbot's own Joël Quenneville (https://www.linkedin.com/in/joel-quenneville-96b18b58/). If you would like to support the show, head over to our GitHub page (https://github.com/sponsors/thoughtbot), or check out our website (https://bikeshed.thoughtbot.com). Got a question or comment about the show? Why not write to our hosts: hosts@bikeshed.fm This has been a thoughtbot (https://thoughtbot.com/) podcast. Stay up to date by following us on social media - YouTube (https://www.youtube.com/@thoughtbot/streams) - LinkedIn (https://www.linkedin.com/company/150727/) - Mastodon (https://thoughtbot.social/@thoughtbot) - Instagram (https://www.instagram.com/thoughtbot/) © 2025 thoughtbot, inc.

    455: Noisy Animals Kata with Fritz Meissner

    Play Episode Listen Later Feb 18, 2025 46:06


    Joël talks with fellow thoughtboter Fritz Meissner about the thinking process behind his latest kata project and the vast world of coding problems. Fritz explains why he developed the noisy animals kata and how it helped to better understand and streamline his code, the best ways to break down conditionals and how to clean them up efficiently within your workflow, as well as knowing where the limits of improvement are in each project you work on. — Refine your conditional logic technique with a copy of 99 Bottles of OOP (https://sandimetz.com/99bottles) and then test your skills with Fritz's Noisy Animals Kata (https://github.com/thoughtbot/noisy-animals-kata). Compare notes with Joël (https://github.com/JoelQ/noisy-animals-kata) and Fritz (https://github.com/thoughtbot/noisy-animals-kata/blob/fm-refactored-v3/noisy_animal.rb) to see how you stack up once you're done! Listen to Joël's RailsConf talk The Math Every Programmer Needs (https://www.youtube.com/watch?v=wzYYT40T8G8) or check out some previous episodes for a refresher on some of the logic and math topics discussed in this show - Ep 398 (https://bikeshed.thoughtbot.com/398) - Ep 353 (https://bikeshed.thoughtbot.com/353) - Ep 418 (https://bikeshed.thoughtbot.com/418) - Ep 428 (https://bikeshed.thoughtbot.com/428) If you'd like to contact Fritz about his Kata or anything else programming related he can be found via LinkedIn (https://www.linkedin.com/in/fritz-meissner-057a4a6/) Your host for this episode has been thoughtbot's own Joël Quenneville (https://www.linkedin.com/in/joel-quenneville-96b18b58/). If you would like to support the show, head over to our GitHub page (https://github.com/sponsors/thoughtbot), or check out our website (https://bikeshed.thoughtbot.com). Got a question or comment about the show? Why not write to our hosts: hosts@bikeshed.fm This has been a thoughtbot (https://thoughtbot.com/) podcast. Stay up to date by following us on social media - YouTube (https://www.youtube.com/@thoughtbot/streams) - LinkedIn (https://www.linkedin.com/company/150727/) - Mastodon (https://thoughtbot.social/@thoughtbot) - Instagram (https://www.instagram.com/thoughtbot/) © 2025 thoughtbot, inc.

    454: Workshop design with Aji Slater

    Play Episode Listen Later Feb 4, 2025 37:46


    Joël is joined by fellow thoughtboter Aji Slater as they discuss their previous experiences in designing content for workshops. Learn how to best structure your workshop for an audience, the benefits of a workshop over a talk and vice versa, as well as how to tackle the different hurdles your audience might face when working through your presentation. — Try your hand at Joël's recommendation of visualising your Git Branching (https://learngitbranching.js.org/). You can watch Ali's Enigma Machine workshop here (https://www.youtube.com/watch?v=KrLVIf-pS4g), Or connect with him via LinkedIn (https://www.linkedin.com/in/doodlingdev/) Your host for this episode has been Joël Quenneville (https://www.linkedin.com/in/joel-quenneville-96b18b58/). If you would like to support the show, head over to our GitHub page (https://github.com/sponsors/thoughtbot), or check out our website (https://bikeshed.thoughtbot.com). Got a question or comment about the show? Why not write to our hosts: hosts@bikeshed.fm This has been a thoughtbot (https://thoughtbot.com/) podcast. Stay up to date by following us on social media - YouTube (https://www.youtube.com/@thoughtbot/streams) - LinkedIn (https://www.linkedin.com/company/150727/) - Mastodon (https://thoughtbot.social/@thoughtbot) - Instagram (https://www.instagram.com/thoughtbot/) © 2025 thoughtbot, inc.

    453: The Bike Shed Wrapped 2024

    Play Episode Listen Later Dec 31, 2024 31:30


    Happy New Year from The Bike Shed! Tune in to the one wrapped edition that really matters this holiday season, The Bike Shed Wrapped! Recap the year with Joël and Stephanie as they reminisce over their favourite moments of 2024. The pair discuss ways they've stepped outside their comfort zone to gain a different perspective on their work, the growth they've each achieved as a result, and their ambitions for 2025 and beyond. Discover Joël and Stephanie's favourite episodes from the year as well as Joël's favourite blog post of 2024 (https://thoughtbot.com/blog/flesh-out-your-conference-talk-idea-using-a-rubric). — Re-listen to Joël and Stephanie's top four episodes of 2024 432: The Semantics and Meaning of Nil (https://bikeshed.thoughtbot.com/432) 435: Cohesive Code with Jared Norman (https://bikeshed.thoughtbot.com/435) 421: The Idealistic Vs. Pragmatic Programmer (https://bikeshed.thoughtbot.com/421) 441: The Pickaxe Book with Noel Rappin (https://bikeshed.thoughtbot.com/441) Want to hear Joël's gnome voice? Watch his RailsConf Talk! (https://www.youtube.com/watch?v=T7GdshXgQZE) Prefer to hear Stephanie give a talk like a regular human? Watch her RailsConf Talk! (https://www.youtube.com/watch?v=VmWCJFiU1oM) Your hosts for this episode have been thoughtbot's own Stephanie Minn and Joël Quenneville (https://www.linkedin.com/in/joel-quenneville-96b18b58/). If you would like to support the show, head over to our GitHub page (https://github.com/sponsors/thoughtbot), or check out our website (https://bikeshed.thoughtbot.com). Got a question or comment about the show? Why not write to our hosts: hosts@bikeshed.fm This has been a thoughtbot (https://thoughtbot.com/) podcast. Stay up to date by following us on social media - YouTube (https://www.youtube.com/@thoughtbot/streams) - LinkedIn (https://www.linkedin.com/company/150727/) - Mastodon (https://thoughtbot.social/@thoughtbot) - Instagram (https://www.instagram.com/thoughtbot/) © 2024 thoughtbot, inc.

    452: Hotwire Essentials with Steve Polito

    Play Episode Listen Later Dec 24, 2024 28:47


    Stephanie is joined by follow thoughtbot-er Steve Polito as they discuss his latest GitHub resource, Botcasts (https://github.com/thoughtbot/botcasts). Find out why Steve was so keen to make the app, what he learnt about Hotwire in the process and why he thinks you should stop listening to the show in your current pod-catcher and pick it up in Botcasts instead! -- Try building Botcasts for yourself over on Github (https://github.com/thoughtbot/botcasts)! Your host for this episode has been thoughtbot's own Stephanie Minn, and was accompanied by Steve Polito. You can find more of Steve's work over on GitHub (https://github.com/stevepolitodesign), or read what he has to say about his work on thoughtbot's blogs (https://thoughtbot.com/blog/process-network-requests-with-turbo). If you want to connect with Steve you can do so through LinkedIn (https://www.linkedin.com/in/steve-polito). Interested in birds instead? Why not check out Stephanie's book recommendation (https://www.mattkracht.com/fieldguidetodumbbirdsofnorthamerica)! If you would like to support the show, head over to our GitHub page (https://github.com/sponsors/thoughtbot), or check out our website (https://bikeshed.thoughtbot.com). Got a question or comment about the show? Why not write to our hosts: hosts@bikeshed.fm This has been a thoughtbot (https://thoughtbot.com/) podcast. Stay up to date by following us on social media - LinkedIn (https://www.linkedin.com/company/150727/) - Mastodon (https://thoughtbot.social/@thoughtbot) - Instagram (https://www.instagram.com/thoughtbot/) © 2024 thoughtbot, inc.

    451: Making Time for and Managing Focus

    Play Episode Listen Later Dec 17, 2024 29:25


    It's officially the holidays at the Bike Shed! Defrag your hard drives and take a break with Joël and Stephanie as they breakdown different ways to manage your focus during the day. The pair discuss separating coding time from thinking time when working, the pros and cons of blocking out time for different tasks and clever ways to move seamlessly from one project to the next without losing momentum. Joël has some more timezone facts to share, while Stephanie reveals her worst enemy when it comes to productivity. — Try out the Pomodoro system (https://en.wikipedia.org/wiki/Pomodoro_Technique) in your workflow and let us know if it works for you! Your hosts for this episode have been thoughtbot's own Stephanie Minn and Joël Quenneville (https://www.linkedin.com/in/joel-quenneville-96b18b58/). If you would like to support the show, head over to our GitHub page (https://github.com/sponsors/thoughtbot), or check out our website (https://bikeshed.thoughtbot.com). Got a question or comment about the show? Why not write to our hosts: hosts@bikeshed.fm This has been a thoughtbot (https://thoughtbot.com/) podcast. Stay up to date by following us on social media - LinkedIn (https://www.linkedin.com/company/150727/) - Mastodon (https://thoughtbot.social/@thoughtbot) - Instagram (https://www.instagram.com/thoughtbot/) © 2024 thoughtbot, inc.

    450: Javascript-Driven Development?

    Play Episode Listen Later Dec 10, 2024 39:57


    Joël and Stephanie go back to fundamentals as they pick apart some recent conversations they've been having around the office. Together they discuss the advantages of GraphQL over a REST API, how they utilise JSONB over a regular column or table, and the use-cases for and against a frontend framework like React. But what's the theme that ties all these conversations together? — The article mentioned in this episode was Why I'm over GraphQL (https://bessey.dev/blog/2024/05/24/why-im-over-graphql/) Your hosts for this episode have been thoughtbot's own Stephanie Minn and Joël Quenneville (https://www.linkedin.com/in/joel-quenneville-96b18b58/). If you would like to support the show, head over to our GitHub page (https://github.com/sponsors/thoughtbot), or check out our website (https://bikeshed.thoughtbot.com). Got a question or comment about the show? Why not write to our hosts: hosts@bikeshed.fm This has been a thoughtbot (https://thoughtbot.com/) podcast. Stay up to date by following us on social media - LinkedIn (https://www.linkedin.com/company/150727/) - Mastodon (https://thoughtbot.social/@thoughtbot) - Instagram (https://www.instagram.com/thoughtbot/) © 2024 thoughtbot, inc.

    449: Evergreen skills for new-ish developers

    Play Episode Listen Later Dec 3, 2024 37:41


    One of the most challenging things about starting out as a developer is how much you need to master all at once. And with so much to learn, it can be difficult for experts to guide fresh developers and advise them on where to focus first. Luckily, some skills will always be useful, no matter what language you're coding in. In today's episode, Stephanie and Joël tackle this topic by unpacking several key evergreen skills that will always be valuable, from reading error messages to deciphering a stack trace. They break down how new-ish developers can start acquiring these skills, key obstacles they're likely to encounter, and how to ask for help when you hit a block. Their conversation covers the ins and outs of debugging, how to feel comfortable in your editor as a new developer, the art of asking for help, and much more. They also share plenty of valuable tips to help you on your journey – including one that will help you commit more frequently. Tune in now to hear it all! Key Points From This Episode: Stephanie's time at the Ruby Conference in Chicago. The challenges of advising new-ish developers as an expert. Broad evergreen skills that are always valuable to learn. Tips on mastering debugging as a core skill. How to improve your ability to read error messages. Our approach to resolving errors and isolating what is wrong. Advice for learning to read a stack trace (even though it's intimidating). Strategies for fixing different types of bugs. The value of editor mastery and version history. Tips on how to commit more frequently as a new developer. Learning to ask for help when you hit a block. The art of structuring your questions when asking for help. Breaking down large tasks into smaller sections. Learning to find focus as a new developer. Links Mentioned in Today's Episode: What technologies should I learn? (https://thoughtbot.com/blog/what-technologies-should-i-learn) Debugging blog post series (https://thoughtbot.com/blog/tags/debugging-series-2021) Asking about solutions rather than problems (https://xyproblem.info/) The Bike Shed (https://bikeshed.thoughtbot.com/) Joël Quenneville on LinkedIn (https://www.linkedin.com/in/joel-quenneville-96b18b58/) Joël Quenneville on X (https://x.com/joelquen) Support The Bike Shed (https://github.com/sponsors/thoughtbot) Mailtrap (https://l.rw.rw/the_bike_shed) WorkOS (workos.com/)

    448: Other Uses for Tests

    Play Episode Listen Later Nov 26, 2024 33:42


    How can tests serve beyond just catching bugs in code? In this episode, Stephanie and Joël dive into the various roles that tests can play in a developer's toolkit. Covering all the fundamentals, from aiding knowledge transfer and documentation to ensuring accountability in code reviews, they explore the unexpected ways that tests support developer workflows. They also explain the balance between writing detailed tests for documentation and managing complex code, and how effective testing practices can help developers become more confident and informed in their work. Gain insights about the impact of test suites on team collaboration, code readability, and project handoffs, and discover how tests can provide a “living specification” that evolves with your application. Join us to learn how to make the most of your tests and unlock new ways to elevate your development process. Tune in now! How test suites can act as living documentation that changes with the codebase. Using tests to document complex code before handing off a project. How backfilling tests can reveal critical edge cases in legacy code. The benefits of tests for developers working with complex code areas. Why a balance between comprehensive coverage and “good enough” testing is essential. Challenges associated with reading tests as documentation in certain codebases. Techniques for improving the readability of test suites for documentation. Advantages of using tests as a tool for accountability in code reviews. The concept of test-first code reviews to improve understanding of pull requests. Links Mentioned in Today's Episode: Mailtrap (https://l.rw.rw/the_bike_shed) 'Unlock the value of tests in understanding your codebase' (https://thoughtbot.com/blog/unlock-the-value-of-tests-in-understanding-your-codebase) Good Enough Testing (https://goodenoughtesting.com/) 'RailsConf 2024: So writing tests feels painful. What now? by Stephanie Minn' (https://youtu.be/t5MnS20adG4) 'Algorithms we develop software by' (https://grantslatton.com/software-pathfinding#algorithms-we-develop-software-by) Exercism (exercism.io) WorkOS (https://workos.com/) The Bike Shed (https://bikeshed.thoughtbot.com/) Joël Quenneville on LinkedIn (https://www.linkedin.com/in/joel-quenneville-96b18b58/) Joël Quenneville on X (https://x.com/joelquen) Support The Bike Shed (https://github.com/sponsors/thoughtbot)

    447: How to (not) implement impersonation

    Play Episode Listen Later Nov 19, 2024 37:39


    For developers, impersonation can be a powerful tool, but with great power comes great responsibility. In today's episode, hosts Stephanie and Joël explore the complexities of implementing impersonation features in software development, giving you the ability to take over someone's account and act as the user. They delve into the pros and cons of impersonation, from how it can help with debugging and customer support to its prime drawbacks regarding security and auditing issues. Discover why the need for impersonation is often a sign of poor admin tooling, alternative solutions to true impersonation, and the scenarios where impersonation might be the most pragmatic approach. You'll also learn why they advocate for understanding the root problem and considering alternative solutions before implementing impersonation. Tune in today for a deep dive into impersonation and the best ways to use it (or not use it)!
 Key Points From This Episode: What's new in Stephanie's world: how Notion Calendar is helping her manage her schedule. Joël's quest to find a health plan: how he used a spreadsheet to compare his options. A client request to build an impersonation feature, and why Joël has mixed feelings about it. What an impersonation tool does: it allows you to take over someone's account. When it's useful to use implementation as a feature, like for debugging and support. Potential risks and responsibilities associated with impersonation. Why the need for impersonation often indicates poor admin tooling. Technical and security implications of impersonation. Solutions for logging the audit trail when you're doing impersonation. Differentiating between the logged-in user and the user you're rendering views for. Building an app that isn't as tightly coupled to the “current user.” Suggested alternatives to true impersonation. The value of cross-functional teams and collaborative problem-solving. Links Mentioned in Today's Episode: Mailtrap (https://l.rw.rw/the_bike_shed) Notion Calendar (https://www.notion.com/product/calendar) 'Implementing Impersonation' (https://jamie.ideasasylum.com/2018/09/29/implementing-impersonation) Sustainable Web Development with Ruby on Rails (https://sustainable-rails.com/) The Bike Shed (https://bikeshed.thoughtbot.com/) Joël Quenneville on LinkedIn (https://www.linkedin.com/in/joel-quenneville-96b18b58/) Joël Quenneville on X (https://x.com/joelquen) Support The Bike Shed (https://github.com/sponsors/thoughtbot) WorkOS (https://workos.com/)

    446: All about rewrites

    Play Episode Listen Later Nov 12, 2024 35:11


    When is it time for a rewrite? How do you justify it? If you're tasked with one, how do you approach it? In today's episode of The Bike Shed, we dive into the tough question of software rewrites, sharing firsthand experiences that reveal why these projects are often more complicated and risky than they first appear. We unpack critical factors that make or break a rewrite, from balancing developer satisfaction with business value to managing stakeholder expectations when costs and timelines stretch unexpectedly. You'll hear about real-world rewrite pitfalls like downtime and reintroducing bugs, as well as strategies for achieving similar improvements through incremental changes or refactoring instead. If you're a developer or team lead considering a rewrite, this conversation offers a pragmatic perspective that could save your team time, effort, and potential setbacks. Tune in to learn how to make the best call for your codebase and find out when a rewrite might actually be necessary! Key Points From This Episode: Accessible selectors versus test IDs: best practices in Capybara and React Testing Library. Balancing test coverage with pragmatism and risk tolerance with Good Enough Testing. Software rewrites and the tough questions around deciding when they're necessary. The importance of prioritizing business value over frustrations with the current codebase. Drawbacks of rewrites, such as downtime, data loss, and reintroducing past bugs. Risks of “grass is greener” thinking and using mocked data in demos. Unrealistic expectations of full feature parity and why an MVP approach is better. How incremental refactoring can achieve similar goals to a complete rewrite. The appeal and hubris of a “fresh start” and why it's much more complex than that. Balancing innovation with practicality: ways to introduce new elements without rewriting. An example that illustrates when a rewrite might actually be necessary. Reasons that early prototypes and test builds are the best candidates for rewrites. Links Mentioned in Today's Episode: Matt Brictson: ‘Simplify your Capybara selectors' (https://mattbrictson.com/blog/simplify-capybara-selectors) React Testing Library Guidelines (https://testing-library.com/docs/queries/about/#priority) Capybara Accessibility Selectors (https://github.com/citizensadvice/capybara_accessible_selectors) Good Enough Testing (https://goodenoughtesting.com/) ‘RailsConf 2023: The Math Every Programmer Needs by Joël Quenneville' (https://youtu.be/fMetBx77vKY) ‘Testing Your Edge Cases' (https://thoughtbot.com/blog/testing-your-edge-cases) 'Working Iteratively' (https://thoughtbot.com/blog/working-iteratively) 'Technical Considerations to Help Scale Your Product' (https://thoughtbot.com/blog/technical-considerations-when-scaling-your-application) Dan McKinley: ‘Choose Boring Technology' (https://mcfunley.com/choose-boring-technology) The Bike Shed (https://bikeshed.thoughtbot.com/) Joël Quenneville on LinkedIn (https://www.linkedin.com/in/joel-quenneville-96b18b58/) Joël Quenneville on X (https://x.com/joelquen) Support The Bike Shed (https://github.com/sponsors/thoughtbot)

    445: Working Iteratively

    Play Episode Listen Later Oct 29, 2024 40:06


    Does having smaller, more frequent iterations help to ease your cognitive load? During this episode, we discuss the benefits and challenges of working iteratively and whether or not it can prevent costly errors. You'll hear about juggling individual pieces effectively, factors that incentivize and de-incentivize working iteratively, and how Joël gauges whether or not a project should be broken up into smaller tasks. It can be hard to adopt small iterations, and this conversation also touches on the idea of ‘good enough code' and discusses how agility can reduce the cost of making changes. Tuning in, you'll hear about some of the challenges of keeping up with changes as they evolve and why it is beneficial to do so. You will also be equipped with a thought experiment involving elephant carpaccio to build your understanding of working iteratively, explore the challenge of keeping up with evolving changes, and more. Thanks for listening. Key Points From This Episode: Stephanie shares a recent mishap that happened at work and what she learned from it. Unpacking pressures and other aspects that may have contributed to the error. Joël's recent travels and his fresh appreciation for fall. The cost of an incident occurring, how this increases, and the role of code review. Benefits and pitfalls of more regular code review. Why working with smaller chunks of work is helpful for Joël's focus. Juggling individual pieces effectively. Factors that de-incentivize working iteratively such as waiting on 24-hour quality control processes. How working iteratively can facilitate better communication. Why Joël feels that work that spans a few days should be broken up into smaller chunks. The idea of ‘good enough code'. How agility can reduce the cost of making changes. Using the elephant carpaccio exercise to bolster your understanding of working iteratively. The challenge of keeping up with changes as they evolve and why it is beneficial to do so. Involvement from the team and the capacity to change course. Links Mentioned in Today's Episode: Working Incrementally (https://bikeshed.thoughtbot.com/361) Working Iteratively (https://thoughtbot.com/blog/working-iteratively) Elephant Carpaccio Exercise (https://docs.google.com/document/u/1/d/1TCuuu-8Mm14oxsOnlk8DqfZAA1cvtYu9WGv67Yj_sSk/pub) The Bike Shed (https://bikeshed.thoughtbot.com/) Joël Quenneville on LinkedIn (https://www.linkedin.com/in/joel-quenneville-96b18b58/) Joël Quenneville on X (https://x.com/joelquen) Support The Bike Shed (https://github.com/sponsors/thoughtbot)

    444: From Solutions To Patterns

    Play Episode Listen Later Oct 15, 2024 34:58


    What's the difference between solving problems and recognizing patterns, and why does it matter for developers? In this episode, Stephanie and Joël discuss transitioning from collecting solutions to identifying patterns applicable to broader contexts in software development. They explore the role of heuristics, common misconceptions among junior and intermediate developers, and strategies for leveling up from a solution-focused mindset to thinking in patterns. They also discuss their experiences of moving through this transition during their careers and share advice for upcoming software developers to navigate it successfully. They explore how learning abstraction, engaging in code reviews, and developing a strong intuition for code quality help developers grow. Uncover the issue of over-applying patterns and gain insights into the benefits of broader, reusable approaches in code development. Join us to discover how to build your own set of coding heuristics, the pitfalls of pattern misuse, and how to become a more thoughtful developer. Tune in now! Key Points From This Episode: Stephanie unpacks the differences between patterns and solutions. The role of software development experience in recognizing patterns. Why transitioning from solving problems to recognizing patterns is crucial. Joël and Stephanie talk about the challenges of learning abstraction. Hear pragmatic strategies for implementing patterns effectively. How junior developers can build their own set of heuristics for code quality. Discover valuable tools and techniques to identify patterns in your work. Find out about approaches to documenting, learning, and sharing patterns. Gain insights into the process of refactoring a solution into a pattern. Outlining the common mistakes developers make and the pitfalls to avoid. Steps for navigating disagreements and feedback in a team environment. Links Mentioned in Today's Episode: RubyConf 2021 - The Intro to Abstraction I Wish I'd Received (https://www.youtube.com/watch?v=m0dC5RmxcFk) 'Ruby Science' (https://thoughtbot.com/ruby-science/introduction.html) Refactoring.Guru (https://refactoring.guru/) Thoughtbot code review guide (https://github.com/thoughtbot/guides/blob/main/code-review/README.md) The Bike Shed (https://bikeshed.thoughtbot.com/) Joël Quenneville on LinkedIn (https://www.linkedin.com/in/joel-quenneville-96b18b58/) Joël Quenneville on X (https://x.com/joelquen) Support The Bike Shed (https://github.com/sponsors/thoughtbot)

    443: Rails World and Open Source with Stefanni Brasil

    Play Episode Listen Later Oct 8, 2024 31:15


    Learning from other developers is an important ingredient to your success. During this episode, Joël Quenneville is joined by Stefanni Brasil, Senior Developer at Thoughtbot, and core maintainer of faker-ruby. To open our conversation, she shares the details of her experience at the Rails World conference in Toronto and the projects she enjoyed seeing most. Next, we explore the challenge of Mac versus Windows and how these programs interact with Ruby on Rails and dive into Stefanni's involvement in Open Source for Thoughtbot and beyond; what she loves about it, and how she is working to educate others and expand the current limitations that people experience. This episode is also dedicated to the upcoming Open Source Summit that Stefanni is planning on 25 October 2024, what to expect, and how you can get involved. Thanks for listening! Key Points From This Episode: Introducing and catching up with Thoughtbot Senior Developer and maintainer of faker-ruby, Stefanni Brasil. Her experience at the Rails World conference in Toronto and the projects she found most inspiring. Why accessibility remains a key topic. How Ruby on Rails translates on Mac and Windows. Stefanni's involvement in Open Source and why she enjoys it. Her experience as core maintainer at faker-ruby. Ideas she is exploring around Jeremy Evans' book Polished Ruby Programming and the direction of Faker. Involvement in Thoughtbot's Open Source and how it drew her in initially. The coaching series on Open Source that she participated in earlier this year. What motivated her to create a public Google doc on Open Source maintenance. An upcoming event: the Open Source Summit. The time commitment expected from attendees. How Stefanni intends to interact with guests and the talk that she will give at the event. Why everyone is welcome to engage at any level they are comfortable with. Links Mentioned in Today's Episode: Stefanni Brasil (https://www.stefannibrasil.me/) Stefanni Brasil on X (https://x.com/stefannibrasil) Thoughtbot Open Summit (https://thoughtbot.com/events/open-summit) Open Source Issues doc (https://docs.google.com/document/d/1zok6snap6T6f4Z1H7mP9JomNczAvPEEqCEnIg42dkU4/edit#heading=h.rq72izdz9oh6) Open Source at Thoughtbot (https://thoughtbot.com/open-source) Polished Ruby Programming (https://www.packtpub.com/en-us/product/polished-ruby-programming-9781801072724) Faker Gem (https://github.com/faker-ruby/faker) Rails World
 (https://rubyonrails.org/world/) The Bike Shed (https://bikeshed.thoughtbot.com/) Joël Quenneville on LinkedIn (https://www.linkedin.com/in/joel-quenneville-96b18b58/)

    442: Paradigms - What is a Program?

    Play Episode Listen Later Oct 1, 2024 42:22


    What is a program? Your answer to this question will determine the paradigm through which you view programming. During this episode, you'll come to understand how things change once you develop an awareness of your paradigm, and what. To kick off this episode, Stephanie shares key insights she took from Planet Argon's 2024 Ruby on Rails survey and dives deeper into her history with Ruby on Rails. Next, we dive into the definition of a paradigm and unpack three different paradigms you might hold as a developer: procedural, object-oriented, and functional. Considering how each of these impacts the way that you might approach your work as a developer, and what you can learn from the ones that are less familiar to you. Joël describes his scripting style and evaluates the concept of pure functions and their place in development, and we close by digging deeper into how your paradigm might impact the code that you write. Tune in to hear all this and more. Key Points From This Episode: The EPI feature that Joël has started to build out for his client. Why Stephanie is excited about the results of Planet Argon's 2024 Ruby on Rails community survey. What a procedural program is: programming envisions a program as a series of instructions to a computer. Defining an object-oriented paradigm: programming envisions a program as the behavior that emerges from objects talking to each other. How a functional paradigm envisions a program as a series of data transformations. Alan Turing and Alonzo Church's approach to understanding this. How a lot of the foundations of computer science came to be built before we had computers. Using Ruby to make judgments and assessing whether or not this is a procedural habit. Why Joël describes his scripting style as being very procedural. Unpacking the meaning of functional programming. Evaluating the concept of pure functions. Considering how your paradigm may impact the Ruby code that you write. Links Mentioned in Today's Episode: 2024 Ruby on Rails Community Survey (https://railsdeveloper.com/survey/2024/) Church-Turing Thesis (https://ocw.mit.edu/courses/24-242-logic-ii-spring-2004/489f7e42fb619645158d7c21a8fb83ad_chuh_trng_thesis.pdf) Dynamic type systems are not inherently more open (https://lexi-lambda.github.io/blog/2020/01/19/no-dynamic-type-systems-are-not-inherently-more-open/) What is Functional Programming? (blog.jenkster.com/2015/12/what-is-functional-programming.html) Blocks as an abstraction vs for loops (https://thoughtbot.com/blog/special-cases) Functional core imperative shell (https://www.destroyallsoftware.com/screencasts/catalog/functional-core-imperative-shell) Testing objects with a functional mindset
 (https://thoughtbot.com/blog/functional-viewpoints-on-testing-objectoriented-code) The Bike Shed (https://bikeshed.thoughtbot.com/) Joël Quenneville on LinkedIn (https://www.linkedin.com/in/joel-quenneville-96b18b58/) Support The Bike Shed (https://github.com/sponsors/thoughtbot)

    441: The Pickaxe Book with Noel Rappin

    Play Episode Listen Later Sep 24, 2024 39:44


    For a long time, Programming Ruby was the authority in the developing world. Now, a much-needed update has been published. During this conversation, we are joined by Noel Rappin, who shares how his frustration at the idea of static type in Ruby motivated him to investigate why he felt this way, as he published his findings in The Pickaxe Book. We discuss how this book differs from previous material he has published, explore a recent blog post series that explored the idea of failing fast, and address the widespread opinion that developers should take a simpler approach that is more accessible. Noel also explores the responsibility of understanding how readers consume material and the importance of providing thorough context as an author, how Programming Ruby became the most significant programming reference, and the surprising journey that led Noel to realize he was able to provide an updated version of the theory in it. Next, we dive into some of the more opinionated blog posts Noel has posted and the harshest feedback he has received in response to them. You'll also hear about his research and learning during the act of writing the book. Join us today to hear all this and more. Key Points From This Episode: Noel Rappin's recently published work, The Pickaxe Book, on current versions of Ruby. The inception of the book during discussions about the collision of Sorbet and Ruby. How his background made him comfortable with the idea that there are no static types. A recent blog post series and how it answered a question about failing fast. Considering whether developers pursue simpler things that are more accessible to a wider range of coders. The problem of thoroughness and longevity in writing instructional material. Developing awareness of how readers consume and contextualize theory and opinion. How Programming Ruby became the most significant programming reference. Noel's updated version of this material in his latest book. His blog posts on real-life applications of Ruby and the feedback he receives. How he goes about framing blog posts as opinion or instruction. Determining what community consensus is. The bewilderment that often accompanies onboarding sessions. Research and learning leading up to writing and publishing the book. Feedback and reviews on the book. Links Mentioned in Today's Episode: Noel Rappin (Noel Rappin) Noel Rappin on X (Noel Rappin on X) Programming Ruby (Programming Ruby) How Not to Use Static Typing in Ruby (How Not to Use Static Typing in Ruby) David Copeland Talk (David Copeland Talk) Better Know a Ruby Thing (Better Know a Ruby Thing) How To Manage Duplicate Test Setup, Or Can I Interest You in Weird RSpec? (How To Manage Duplicate Test Setup, Or Can I Interest You in Weird RSpec?) Better Know a Ruby Thing: On The Use of Private Methods (Better Know a Ruby Thing: On The Use of Private Methods) Standardrb (Standardrb) Rails Test Prescriptions (Rails Test Prescriptions) Programming Ruby: A Pragmatic Programmer's Guide (Programming Ruby: A Pragmatic Programmer's Guide) The Bike Shed (The Bike Shed) Joël Quenneville on LinkedIn (Joël Quenneville on LinkedIn) Support The Bike Shed (Support The Bike Shed)

    440: When we stray from Rails defaults

    Play Episode Listen Later Sep 17, 2024 42:56


    When does it make sense to step away from Rails conventions? What are the limits of convention over configuration? While Rails conventions provide a solid foundation, there are times when customization is necessary to meet specific project needs. In this episode, Joël and Stephanie dive into the tradeoffs of breaking away from Rails defaults. They explore the limits of convention over configuration and share their experiences with customizing beyond the typical Rails setup. Joël offers insights from a recent project where the client opted for all dry-rb objects, and they unpack the benefits and potential challenges of this approach. Stephanie talks about why people tend to shy away from certain Ruby features and her lessons regarding leveraging callbacks for code development. Explore different testing frameworks, the situations when following Ruby defaults is better, the benefits of the ActiveModel ecosystem, and more! Whether you are a Rails purist or looking to bend the rules, this episode will help you understand the pros and cons of stepping outside the Ruby on Rails box. Don't miss it! Key Points From This Episode: Joël shares details about a large-scale refactoring initiative he has been working on. Stephanie's recent legacy-code production problem and lessons from her experience. What Joël would have done differently when building his refactoring initiative. The problems of renaming background applications during code development. Why the open-close principle is valuable for making class changes to a system. Reasons that a migration strategy is vital for navigating new and legacy code. Explore approaches for overcoming synchronization issues between systems. Learn about the concept of connascence for coupling systems together. Considerations for using asynchronous tools with a connascence approach. Practical ways to maintain naming consistency during code development. The importance of differentiating between web and business-logic layers. Situations where relying on callbacks for connascence becomes problematic. Other issues that callback problems can reveal during code development. Joël unpacks the scenarios where he deviates from the Ruby on Rails standard. Frameworks for testing code and final takeaways from Joël and Stephanie. Links Mentioned in Today's Episode: 'Refactoring Legacy Code with the Strangler Fig Pattern' (https://shopify.engineering/refactoring-legacy-code-strangler-fig-pattern) Connascence of Name (CoN) (https://thoughtbot.com/blog/connascence-as-a-vocabulary-to-discuss-coupling#connascence-of-name-con) ActiveModel docs (https://guides.rubyonrails.org/active_model_basics.html) GitHub | activemodel (https://github.com/rails/rails/tree/main/activemodel) 'Vanilla Rails is plenty' (https://dev.37signals.com/vanilla-rails-is-plenty/) GitHub | minitest (https://github.com/seattlerb/minitest) GitHub | test-unit (https://github.com/test-unit/test-unit) Episode 435: Cohesive Code with Jared Norman (https://bikeshed.thoughtbot.com/435) Ruby on Rails
The Bike Shed (https://rubyonrails.org) Joël Quenneville on LinkedIn (https://www.linkedin.com/in/joel-quenneville-96b18b58/) Support The Bike Shed (https://github.com/sponsors/thoughtbot)

    439: Async Ruby & Rails with Trevor Turk

    Play Episode Listen Later Sep 10, 2024 34:11


    How can asynchronous programming transform your Ruby on Rails applications? Today, Stephanie sits down with Hello Weather co-creator Trevor Turk to unpack asynchronous programming in Ruby on Rails. Trevor Turk is a seasoned software developer known for his work on Hello Weather, a minimalist weather app that delivers essential weather data quickly and precisely. He's also the creator of Weather Machine, an advanced weather data platform designed to serve reliable and highly accurate forecasts via API. With a background that includes work at innovative tech companies, Trevor brings years of experience in developing intuitive, user-friendly digital tools. Trevor talks about the focus of his API work, the complexity of web-based apps, and what makes Hello Weather unique. He explains the fundamentals of asynchronous programming within the Ruby on Rails framework and why it is an approach all programmers should consider. Explore the nuances of programming for different data sources, how he leverages fibers and threads for the Hello Weather platform, and why asynchronous programming is not a silver bullet for application development. Discover how to start using asynchronous methods, the various asynchronous tools available in Ruby, and why experimenting with concurrent programming is essential. Join us to gain insights into why including asynchronous tools is vital for the Ruby on Rails ecosystem, improving platforms through open-source development, how to help improve the adoption of asynchronous tools in Ruby, and more. Tune in now! Key Points From This Episode: Introduction to Turk and his background in Ruby on Rails. Details about his companies Hello Weather and Weather Machine. The innovative features that the Hello Weather platform offers. Hear how Hello Weather transitioned from a web-based to an application. Why he needed to alter his programming approach to scale the company. How he came across the concept of asynchronous programming. Discover how using fibers is different from using threads in Ruby. Find out about the different use cases of asynchronous programming. Learn about the benefits of implementing concurrent programming. Trevor shares the challenges of working with different versions of Ruby. His role in enhancing asynchronous methods within the Ruby framework. Common misconceptions of working with Ruby on Rails. Final takeaways for those interested in asynchronous programming. Links Mentioned in Today's Episode: Trevor Turk on LinkedIn (https://www.linkedin.com/in/trevorturk/) Trevor Turk on X (https://x.com/trevorturk) Trevor Turk on Threads (https://www.threads.net/@trevorturk) Hello Weather (https://helloweather.com/) Weather Machine (https://weathermachine.io) GitHub | async gem (https://github.com/socketry/async) GitHub | falcon gem (https://github.com/socketry/falcon) 'Async Ruby on Rails' (https://thoughtbot.com/blog/async-ruby-on-rails) load_async (https://api.rubyonrails.org/classes/ActiveRecord/Relation.html#method-i-load_async) Episode 437: Contributing to Open Source in the Midst of Daily Work with Steve Polito (https://bikeshed.thoughtbot.com/437) GitHub | Action Cable server adapter (https://github.com/rails/rails/pull/50979) ActiveRecord connection checkout caching (https://github.com/rails/rails/pull/50793) Ruby on Rails
The Bike Shed (https://rubyonrails.org/) The Bike Shed (https://bikeshed.thoughtbot.com/) Joël Quenneville on LinkedIn (https://www.linkedin.com/in/joel-quenneville-96b18b58/) Support The Bike Shed (https://github.com/sponsors/thoughtbot)

    438: Writing abstractions in tests

    Play Episode Listen Later Sep 3, 2024 49:08


    Writing abstractions in tests can be surprisingly similar to storytelling. The most masterful stories are those where the author has stripped away all of the extra information, and given you just enough knowledge to be immersed and aware of what is going on. But striking that balance can be tricky, both in storytelling and abstractions in tests. Too much information and you risk overwhelming the reader. Too little and they won't understand why things are operating the way they are. Today, Stephanie and Joël get into some of the more controversial practices around testing, why people use them, and how to strike the right balance with your information. They discuss the most common motivations for introducing abstractions, from improved readability to simplifying the test's purpose and the types of tests where they are most likely to introduce abstractions. Our hosts also reflect on how they feel about different abstractions in tests – like custom matchers and shared examples – outlining when they reach for them, and the tradeoffs and benefits that come with each. To learn more about how to find the perfect level of abstraction, be sure to tune in! Key Points From This Episode: What's new in Joël's world; mocking out screens for processes or a new bit of UI. The new tool Stephanie's using for reading on the web: Reader by Readwise. Today's topic: controversial practices around testing. How Stephanie and Joël feel about looping through arrays and having IT blocks for each. The most common motivations for introducing abstractions or helper methods into your tests. Pros and cons of factories as abstractions in testing. Types of tests where Joël and Stephanie are more likely to introduce abstractions. Using page objects in system tests to improve user experience. Finding the balance between too little and too much information with abstraction in testing. Why Stephanie has been enjoying fancier matchers like RSpecs. Top uses of custom matchers, especially for specialized error messaging. Why Stephanie prefers custom matchers over shared examples. Using helper methods as a lighter version of abstraction. Differences and similarities between abstractions in tests versus application code. A reminder to keep your goals in mind when using abstraction. Links Mentioned in Today's Episode: Reader by Readwise (https://readwise.io/read) Why factories (https://thoughtbot.com/blog/why-factories) Why not factories (https://thoughtbot.com/blog/speed-up-tests-by-selectively-avoiding-factory-bot) Capybara at a single level of abstraction (https://thoughtbot.com/blog/acceptance-tests-at-a-single-level-of-abstraction) Writing custom RSpec matchers (https://thoughtbot.com/blog/acceptance-tests-at-a-single-level-of-abstraction) Value objects shared examples (https://thoughtbot.com/blog/value-object-semantics-in-ruby) 'DRY is about knowledge' (https://verraes.net/2014/08/dry-is-about-knowledge/) Joël Quenneville on LinkedIn (https://www.linkedin.com/in/joel-quenneville-96b18b58/)

    437: Contributing to Open Source in the Midst of Daily Work with Steve Polito

    Play Episode Listen Later Aug 27, 2024 35:28


    Are you passionate about open source but struggling to find time amidst your daily work? Today on the podcast, Joël Quenneville sits down with Steve Polito to discuss practical strategies for making meaningful contributions to the open-source community, even when your schedule is packed. Steve is a developer with extensive experience in the open-source world seamlessly. He's known for his ability to integrate open-source contributions into his daily workflow, all while maintaining high productivity in his professional role. In our conversation, we explore balancing professional responsibilities with open-source contributions. Steve walks us through his process, from the importance of keeping notes to leveraging Rails issue templates. Discover strategies for contributing to open-source work during work hours, the benefits of utilizing existing processes, and why extending the success of your work to the larger developer community is essential. Join us to hear recommendations for handling pull requests with Ruby on Rails, tips for using reproduction scripts, why you should release reports early and often, and much more. Tune in and learn how to seamlessly integrate open-source contributions into your daily workflow with Steve Polito! Key Points From This Episode: Joël and Steve catch up and share what they are currently working on. Transitioning synchronous processing in a web request to the background. An update on Steve's “building in public” approach and its reception at thoughtbot. How Steve chooses to document and track his development process. Find out how he uses templates to enhance and increase productivity. Why open-source work does not need to be done during your free time. Ways you can contribute to open-source projects during normal work hours. The benefits of sharing troubleshooting solutions with the open-source community. Pull request lessons from his time working with Ruby on Rails. Reasons why issues have a lower barrier to entry with Ruby on Rails. His unique approach of using issues, pull requests, and suspenders. Identifying aspects of everyday work that are suitable for open-source projects. Links Mentioned in Today's Episode: Steve Polito Steve Polito on X Episode 351: Learning in Public Rails issue templates Suspenders Mermaid Ruby on Rails WorkOS thoughtbot
The Bike Shed Joel Quenneville on LinkedIn Support The Bike Shed

    436: Creating Conditions For Your Best Work with Steph Viccari

    Play Episode Listen Later Aug 13, 2024 43:09


    How can we optimize our time and environment to do our best work as developers? In today's episode, we are joined by Stephanie Viccari, former co-host of The Bike Shed and Senior Developer at thoughtbot, to unpack the steps for creating work conditions that enhance productivity. In this conversation, we delve into her unique communication style and approach to optimizing productivity within a team. She explains why she decided to hang up her consulting hat and join the product team at Cisco Meraki, her new role there, and how her consulting skills benefit her new position. Tuning in, you'll discover the key to empathetic communication, how to unblock yourself, tips to help you navigate different communication styles, and why you should advocate for your needs. Stephanie also shares strategies for effective communication and recommendations for managing ‘deep work' when your time is limited. Gain valuable insights into how to uncover what makes your skillset unique, why it takes a team to manage complex software, benchmarking performance, keeping motivated during stressful times, and more. To learn how to create the conditions for your best work and unlock your full potential as a developer, don't miss this episode with Stephanie Viccari! Key Points From This Episode: Catch up with Stephanie: what she's been up to since leaving thoughtbot. How she mastered optimizing workflows and enhancing productivity. Similarities and differences between working as a consultant versus on a product team. Ways Stephanie's mindset shifted from individual thinking to team-oriented strategies. Nuances of advocating for changes as a consultant versus within a product team. What software developers need to achieve their best work. The role of trust between managers and developers in effective problem-solving. Tips and recommendations for identifying and delivering your best work. Practical advice for doing your best work, even when you feel demotivated. Why it's important not to steal from tomorrow's productivity. Links Mentioned in Today's Episode: Stephanie Viccari Stephanie Viccari on LinkedIn Stephanie Viccari on X Stephanie Viccari on GitHub Cisco Meraki thoughtbot Stephanie Viccari's The Bike Shed's Episodes ‘Generative AI is not going to build your engineering team for you'
The Bike Shed Joel Quenneville on LinkedIn Support The Bike Shed

    435: Cohesive Code with Jared Norman

    Play Episode Listen Later Jul 30, 2024 28:45


    How easy is it for a layperson to understand your systems? Jared Norman is a software consultant, speaker, and host of the Dead Code Podcast who specializes in building e-commerce applications in Ruby on Rails. This episode follows two recent talks at RailsConf and covers a theme that emerged from both of them: coupling and cohesion. Tuning in, you'll gain insights on how to create more cohesive components to allow for change and improve your understanding of value objects, systems, and more. You'll also hear about navigating the complexity of domain-driven design and learn how to gauge if your code is easy to understand through a simple rule of thumb. We discuss what it might look like to improve the cohesion of individual objects, identify your systems' seams to create simplicity, and the liminal space between inheritance and composition and the role of decorators in moving through it. Join us today to hear all this and more! Key Points From This Episode: Introducing Jared Norman recent speaker at RailsConf and Ruby on Rails specialist. Jared's interests outside of coding: cycling. Themes that emerged from Jared and Stephanie's talks: coupling and cohesion. A rule of thumb for achieving high cohesion. How value objects tie into the idea of cohesion. Creating more cohesive components in order to have code and systems that are easier to change. The relationships between objects in increasing cohesion and how complex nestings of objects can hinder this. Rearranging systems in order to find seams and create cohesion. Simplifying code in order to facilitate it working independently to support functionality. Improving systems by identifying opportunities for decoupling and other relationships. Inheritance, composition, and decorators and the liminal space between. The complexity of domain-driven design. A rule that indicates when a system is easy to understand. Links Mentioned in Today's Episode: Jared Norman Jared Norman on X The Most Useful Design Pattern Dead Code So Writing Tests Feel Painful. What Now? Dungeons & Dragons & Rails by Joël Quenneville Building Reusable Object-Oriented Systems: Composition Debugging at the Boundaries Working Effectively with Legacy Code Growing Object-Oriented Software Guided by Tests What's in a Name
The Bike Shed Joel Quenneville on LinkedIn Support The Bike Shed

    434: Git and GitHub Workflows

    Play Episode Listen Later Jul 23, 2024 47:42


    It's Calls for Proposals (CFP) season, and in the process of helping our friends and colleagues flesh out their CFPs, we came up with a few questions to help them frame their proposals for success. After learning about the importance of finding your audience and angle of approach for your CFP, we dive into today's main topic – our Git and GitHub workflows. Joel and Stephanie walk us through their current workflows before exploring the differences between main branch and future branch commits. Then, we explore commits editing and why it's okay to make mistakes, commit messages versus GitHub pull requests (PR), what you need to know if you're new to Git, and what you need to understand about PR sizes and Git merge strategies. To end, Joel shares the commit messages that satisfy him the most, and we discover how to make one's life easier when reviewing PRs. Key Points From This Episode: Our CFP framework of questions to help you build a winning proposal. Why it's important to understand who your audience is and who you're speaking to. Ascertaining your angle of approach - how will you tell your story? The ins and outs of Stephanie's current work life. How discipline and particularly, self-discipline relate to our Git and GitHub workflows. Understanding Joel and Stephanie's workflows - how they're similar and how they differ. The differences between main branch and future branch commits. Editing commits and editing commits history, and why it's okay to make mistakes. Commit messages versus GitHub pull requests (PR). Some advice and strategies for those who are new to Git. Discussing Git merge strategies, PR sizes, and online changes. Joel details the types of commit messages that he finds most satisfying. How to make your life easier when reviewing PRs. Links Mentioned in Today's Episode: RubyConf Rubric 'Working Iteratively' Good Commit Messages Shotgun Surgery 'Episode 401: Making the Right Thing Easy' Joel Quenneville on X Joel Quenneville on LinkedIn Support The Bike Shed

    433: Riffing with Kasper Timm Hansen

    Play Episode Listen Later Jul 16, 2024 37:20


    Have you ever wondered how improvisation can revolutionize coding? In today's episode, Stephanie sits down with Kasper Timm Hansen to discuss his innovative “riffing” approach to code development. Kasper is a long-time Ruby developer and former member of the Rails core team. He focuses on Ruby and domain modeling, developing various Ruby gems, and providing consulting services in the developer space. He has become renowned for his approach of “riffing” to software development, particularly in the Ruby on Rails framework. In our conversation, we delve into his unique approach to coding, how it differs from traditional methods, and the benefits of improvisation to code development. Discover the “feeling” part of riffing, the steps to uncovering relationships between models, and why it is okay not to know how to do something. Explore how riffing enhances collaboration, improves communication with and between teams, identifies alternative code, why “clever code” does not make for good solutions, and much more! Tune in to learn how to take your coding skills to the next level and uncover the magic of riffing with Kasper Timm Hansen! Key Points From This Episode: Introduction to Kasper, his background in Ruby, and experience as a consultant. An overview of his RailsConf 2024 presentation on domain modeling. His motivation behind his presentation and the overall reception of the concept. Unpack the concept of “riffing” with code as a developer. Insights into his methodology and how it differs from traditional approaches. Examples of “riffing" and how it benefits the development process. How he determines the best code to implement during his process. Kasper shares how he frames problems and builds solutions. Ways riffing highlights gaps in skillsets early in the development process. Hear about the various ways riffing fosters and improves collaboration. Unpack how riffing can help developers communicate more effectively. Balancing the demands of code review with the riffing approach. Final takeaways for listeners and how to contact Kasper to begin riffing! Links Mentioned in Today's Episode: Kasper on Github (https://github.com/kaspth), Mastodon (https://ruby.social/@kaspth), LinkedIn (https://www.linkedin.com/in/kasper-timm-hansen-33b151314/), and X (https://twitter.com/kaspth) Riffing on Rails RailsConf talk (https://www.youtube.com/watch?v=vH-mNygyXs0) and slides (https://speakerdeck.com/kaspth/railsconf-2024-riffing-on-rails-sketch-your-way-to-better-designed-code) Riffing on Spotify's generated mixes (https://www.youtube.com/watch?v=i1MM2EOniPg) with Jeremy Smith Modeling a Kanban board with riffing (https://buttondown.email/kaspth/archive/how-to-approach-modelling-a-kanban-board-in-rails/) Some of Kasper's open source work: * ActiveRecord Associated Object (https://github.com/kaspth/active_record-associated_object) * ActiveJob Performs (https://github.com/kaspth/active_job-performs) * Oaken (https://github.com/kaspth/oaken)

    432: The Semantics and Meaning of Nil

    Play Episode Listen Later Jul 9, 2024 38:13


    The term ‘nil' refers to the absence of value, but we often imbue it with much more meaning than just that. Today, hosts Joël and Stephanie discuss the various ways we tend to project extra semantics onto nil and the implications of this before unpacking potential alternatives and trade-offs. Joël and Stephanie highlight some of the key ways programmers project additional meaning onto nil (and why), like when it's used to create a guest session, and how this can lead to bugs, confusion, and poor user experiences. They discuss solutions to this problem, like introducing objects for improved readability, before taking a closer look at the implications of excessive guard clauses in code. Our hosts also explore the three-state Boolean problem, illustrating the pitfalls of using nullable Booleans, and why you should use default values in your database. Joël then shares insights from the Elm community and how it encourages rigorous checks and structured data modeling to manage nil values effectively.

They advocate for using nil only to represent truly optional data, cautioning against overloading nil with additional meanings that can compromise code clarity and reliability. Joël also shares a fun example of modeling a card deck, explaining why you might be tempted to add extra semantics onto nil, and why the joker always inevitably ends up causing chaos!
 Key Points From This Episode The project Joël is working on and why he's concerned about bugs and readability. Potential solutions for a confusing constant definition in a nested module. A client work update from Stephanie: cleaning up code and removing dead dependencies. How she used Figjam to discover dependencies and navigate her work. Today's topic: how programmers project extra semantics onto nil. What makes nil really tricky to use, like forcing you to go down a default path. How nil sweeps the cases you don't want to think too hard about under the rug. Extra semantics that accompany nil (that you might not know about) like a guest session. Examples of how these semantics mean different things in different contexts. How these can lead to bugs, hard-to-find knowledge, confusion, and poor user experiences. Introducing objects to replace extra nil semantics, improve readability, and other solutions. Some of the reasons why programmers tend to project extra semantics onto nil. How to notice that nil has additional meanings, and when to model it differently. The implications of excessive guard clauses in code. An overview of the three-state Boolean problem with nullable Booleans. Connecting with the Elm community: how it can help you conduct more rigorous checks. Some of the good reasons to have nil as a value in your database. The benefits of using nil only to represent truly optional data. Links Mentioned in Today's Episode Figjam (https://www.figma.com/figjam/) Miro (https://miro.com/) 'Working Iteratively' blog post (https://thoughtbot.com/blog/working-iteratively) Mermaid.js (https://mermaid.js.org/) Draw.io (https://draw.io/) Check your return values (web) (https://thoughtbot.com/blog/check-return-values-web) Check your return values (API) (https://thoughtbot.com/blog/check-return-values-api) Primitive obsession (https://wiki.c2.com/?PrimitiveObsession) 'Avoid the Three-state Boolean Problem' (https://thoughtbot.com/blog/avoid-the-threestate-boolean-problem) Elm Community (https://elm-lang.org/community) 'The Shape of Data': Modeling a deck of cards (https://thoughtbot.com/blog/modeling-with-union-types#the-shape-of-data) The Bike Shed (https://bikeshed.thoughtbot.com/) Joël Quenneville on LinkedIn (https://www.linkedin.com/in/joel-quenneville-96b18b58/)

    431: Developers Are Professional Question Askers

    Play Episode Listen Later Jul 2, 2024 38:54


    Stephanie shares her newfound interest in naming conventions, highlighting a resource called "Classnames" that provides valuable names for programming and design. Joël, in turn, talks about using AI to generate names for D&D characters, emphasizing how AI can help provide inspiration and reasoning behind name suggestions. Then, they shift to Joël's interest in Roman history, where he discusses a blog by a Roman historian that explores distinctions between state and non-state peoples in the ancient Mediterranean. Together, the hosts delve into the importance of asking questions as consultants and developers to understand workflows, question assumptions, and build trust for better onboarding. Stephanie categorizes questions by engagement stages and their social and technical aspects, while Joël highlights how questioning reveals implicit assumptions and speeds up learning. They stress maintaining a curious mindset, using questions during PR reviews, and working with junior developers to foster collaboration. They conclude with advice on documenting answers and using questions for continuous improvement and effective decision-making in development teams. Class names inspiration (https://classnames.paulrobertlloyd.com/) How to Raise a Tribal Army in Pre-Roman Europe, Part II: Government Without States (https://acoup.blog/2024/06/14/collections-how-to-raise-a-tribal-army-in-pre-roman-europe-part-ii-government-without-states/) Diocletian, Constantine, Bedouin Sayings, and Network Defense (https://www.youtube.com/watch?v=qCUI5ryyMSE) The Power of Being New: A Proven Recipe for High Impact (https://hazelweakly.me/blog/the-power-of-being-new--a-proven-recipe-for-high-impact/#the-power-of-being-new-a-proven-recipe-for-high-impact) How to ask good questions (https://jvns.ca/blog/good-questions/) Transcript:  JOËL: Hello and welcome to another episode of the Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I'm Joël Quenneville. STEPHANIE: And I'm Stephanie Minn. And together, we're here to share a bit of what we've learned along the way. JOËL: So, Stephanie, what's new in your world? STEPHANIE: So, if it has not been clear about just kind of the things I'm mentioning on the podcast the past few weeks, I've been obsessed with naming things lately [chuckles] and just thinking about how to name things, and, yeah, just really excited about...or even just having fun with that more than I used to be as a dev. And I found a really cool resource called "Classnames." Well, it's like just a little website that a designer and developer shared from kind of as an offshoot from his personal website. I'll link it in the show notes. But it's basically just a list of common names that are very useful for programming or even design. It's just to help you find some inspiration when you're stuck trying to find a name for something. And they're general or abstract enough that, you know, it's almost like kind of like a design pattern but a naming pattern [laughs], I suppose. JOËL: Ooh. STEPHANIE: Yeah, right? And so, there's different categories. Like, here's a bunch of words that kind of describe collections. So, if you need to find the name for a containment or a group of things, here's a bunch of kind of words in the English language that might be inspiring. And then, there's also other categories like music for describing kind of the pace or arrangement of things. Fashion, words from fashion can describe, like, the size of things. You know, we talk about T-shirt sizes when we are estimating work. And yeah, I thought it was really cool that there's both things that draw on, you know, domains that most people know in real life, and then also things that are a little more abstract. But yeah, "Classnames" by Paul Robert Lloyd — that's been a fun little resource for me lately. JOËL: Very cool. Have you ever played around at all with using AI to help you come up with the naming? STEPHANIE: I have not. But I know that you and other people in my world have been enjoying using AI for inspiration when they feel a little bit stuck on something and kind of asking like, "Oh, like, how could I name something that is, like, a group of things?" or, you know, a prompt like that. I suspect that that would also be very helpful. JOËL: I've been having fun using that to help me come up with good names for D&D characters, and sometimes they're a little bit on the nose. But if I sort of describe my character, and what's their vibe, and a little bit of, like, what they do and their background, and, like, I've built this whole, like, persona, and then, I just ask the AI, "Hey, what might be some good names for this?" And the AI will give me a bunch of names along with some reasoning for why they think that would be a good match. So, it might be like, oh, you know, the person's name is, I don't know, Starfighter because it evokes their connection to the night sky or whatever because that was a thing that I put in the background. And so, it's really interesting. And sometimes they're, like, just a little too obvious. Like, you don't want, you know, Joe Fighter because he's a fighter. STEPHANIE: And his name is Joe [laughs]. JOËL: Yeah, but some of them are pretty good. STEPHANIE: Cool. Joël, what's new in your world? JOËL: I guess in this episode of how often does Joël think about the Roman Empire... STEPHANIE: Oh my gosh [laughs]. JOËL: Yes [laughs]. STEPHANIE: Spoiler: it's every day [laughs]. JOËL: Whaaat? There's a blog that I enjoy reading from a Roman historian. It's called "A Collection of Unmitigated Pedantry", acoup.blog. He's recently been doing an article series on not the Romans, but rather some of these different societies that are around them, and talking a little bit about a distinction that he calls sort of non-state peoples versus states in the ancient Mediterranean. And what exactly is that distinction? Why does it matter? And those are terms I've heard thrown around, but I've never really, like, understood them. And so, he's, like, digging into a thing that I've had a question about for a while that I've been really appreciating. STEPHANIE: Can you give, like, the reader's digest for me? JOËL: For him, it's about who has the ability to wield violence legitimately. In a state, sort of the state has a monopoly on violence. Whereas in non-state organizations, oftentimes, it's much more personal, so you might have very different sort of nobles or big men who are able to raise, let's say, private armies and wage private war on each other, and that's not seen as, like, some, like, big breakdown of society. It's a legitimate use of force. It's just accepted that that's how society runs. As opposed to in a state, if a, you know, wealthy person decided to raise a private army, that would be seen as a big problem, and the state would either try to put you down or, like, more generally, society would, like, see you as having sort of crossed a line you shouldn't have crossed. STEPHANIE: Hmm, cool. I've been reading a lot of medieval fantasy lately, so this is kind of tickling my brain in that way when I think about, like, what drives different characters to do things, and kind of what the consequences of those things are. JOËL: Right. I think it would be really fascinating to sort of project this framework forwards and look at the European medieval period through that lens. It seems to me that, at least from a basic understanding, that the sort of feudal system seems to be very much in that sort of non-state category. So, I'd be really interested to see sort of a deeper analysis of that. And, you know, maybe he'll do an addendum to this series. Right now, he's mostly looking at the Gauls, the Celtiberians, and the Germanic tribes during the period of the Roman Republic. STEPHANIE: Cool. Okay. Well, I also await the day when you somehow figure how this relates to software [laughter] and inevitably make some mind-blowing connection and do a talk about it [laughs]. JOËL: I mean, theming is always fun. There's a talk that I saw years ago at Strange Loop that was looking at the defense policy of the Roman Emperor Diocletian and the Roman Emperor Constantine, and the ways that they sort of defended the borders of the empire and how they're very different, and then related it to how you might handle network security. STEPHANIE: Whaat? JOËL: And sort of like a, hey, are we using more of a Diocletian approach here, or are we using more of a Constantine approach here? And all of a sudden, just, like, having those labels to put on there and those stories that went with it made, like, what could be a really, like, dry security talk into something that I still remember 10 years later. STEPHANIE: Yeah. Yeah. We love stories. They're memorable. JOËL: So, I'll make sure to link that in the show notes. STEPHANIE: Very cool. JOËL: We've been talking a lot recently about my personal note system, where I keep a bunch of, like, small atomic notes that are all usually based around a single thesis statement. And I was going through that recently, and I found one that was kind of a little bit juicy. So, the thesis is that consultants are professional question-askers. And I'm curious, as a consultant yourself, how do you feel about that idea? STEPHANIE: Well, my first thought would be, how do I get paid to only ask in questions [laughs] or how to communicate in questions and not do anything else [laughs]? It's almost like I'm sure that there is some, like, fantasy character, you know, where it's like, there's some villain or just obstacle where you have this monster character who only talks in questions. And it's like a riddle that you have to solve [laughs] in order to get past. JOËL: I think it's called a three-year-old. STEPHANIE: Wow. Okay. Maybe a three-year-old can do my job then [laughter]. But I do think it's a juicy one, and it's very...I can't wait to hear how you got there, but I think my reaction is yes, like, I do be asking questions [laughs] when I join a project on a client team. And I was trying to separate, like, what kinds of questions I ask. And I kind of came away with a few different categories depending on, like, the stage of the engagement I'm in. But, you know, when I first join a team and when I'm first starting out consulting for a team, I feel like I just ask a lot of basic questions. Like, "Where's the Jira board [laughs]?" Like, "How do you do deployments here?" Like, "What kind of Git process do you use?" So, I don't know if those are necessarily the interesting ones. But I think one thing that has been nice is being a consultant has kind of stripped the fear of asking those questions because, I don't know, these are just things I need to know to do my work. And, like, I'm not as worried about, like, looking dumb or anything like that [laughs]. JOËL: Yeah. I think there's often a fear that asking questions might make you look incompetent or maybe will sort of undermine your appearance of knowing what you're talking about, and I think I've found that to be sort of the opposite. Asking a lot of questions can build more trust, both because it forces people to think about things that maybe they didn't think about, bring to light sort of implicit assumptions that everyone has, and also because it helps you to ramp up much more quickly and to be productive in a way that people really appreciate. STEPHANIE: Yeah. And I also think that putting those things in, like, a public and, like, documented space helps people in the future too, right? At least I am a power Slack searcher [laughs]. And whenever I am onboarding somewhere, one of the first places I go is just to search in Slack and see if someone has asked this question before. I think the next kind of category of question that I discerned was just, like, questions to understand how the team understands things. So, it's purely just to, like, absorb kind of like perspective or, like, a worldview this team has about their codebase, or their work, or whatever. So, I think those questions manifest as just like, "Oh, like, you know, I am curious, like, what do you think about how healthy your codebase is? Or what kinds of bugs is your team, like, dealing with?" Just trying to get a better understanding of like, what are the challenges that this team is facing in their own words, especially before I even start to form my own opinions. Well, okay, to be honest, I probably am forming my own opinions, like, on the side [laughs], but I really try hard to not let that be the driver of how I'm showing up and especially in the first month I'm starting on a new team. JOËL: Would you say these sorts of questions are more around sort of social organization or, like, how a team approaches work, that sort of thing? Or do you classify more technical questions in this category? So, like, "Hey, tell me a little bit about your philosophy around testing." Or we talked in a recent episode "What value do you feel you get out of testing?" as a question to ask before even, like, digging into the implementation. STEPHANIE: Yeah, I think these questions, for me, sit at, like, the intersection of both social organization and technical questions because, you know, asking something like, "What's the value of testing for your team?" That will probably give me information about how their test suite is like, right? Like, what kinds of tests they are writing and kind of the quality of them maybe. And it also tells me about, yeah, like, maybe the reasons why, like, they only have just unit tests or maybe, like, just [inaudible 12:31] test, or whatever. And I think all of that is helpful information. And then, that's actually a really...I like the distinction you made because I feel like then the last category of questions that I'll mention, for now, feels like more geared towards technical, especially the questions I ask to debunk assumptions that might be held by the team. And I feel like that's like kind of the last...the evolution of my question-asking. Because I have, hopefully, like, really absorbed, like, why, you know, people think the way they do about some of these, you know, about their code and start to poke a little bit on being like, "Why do you think, you know, like, this problem space has to be modeled this way?" And that has served me well as a consultant because, you know, once you've been at an organization for a while, like, you start to take a lot of things for granted about just having to always be this way, you know, it's like, things just are the way they are. And part of the power of, you know, being this kind of, like, external observer is starting to kind of just like, yeah, be able to question that. And, you know, at the end of day, like, we choose not to change something, but I think it's very powerful to be able to at least, like, open up that conversation. JOËL: Right. And sometimes you open up that conversation, and what you get is a link to a big PR discussion or a Wiki or something where that discussion has already been had. And then, that's good for you and probably good for anybody else who has that question as well. STEPHANIE: I'm curious, for you, though, like, this thesis statement, atomic note, did you have notes around it, or was it just, like, you dropped it in there [laughs]? JOËL: So, I have a few things, one is that when you come in as a consultant, and, you know, we're talking here about consultants because that's what we do. I think this is probably true for most people onboarding, especially for non-junior roles where you're coming in, and there's an assumption of expertise, but you need to onboard onto a project. This is just particularly relevant for us as consultants because we do this every six months instead of, you know, a senior developer who's doing this maybe every two to three years. So, the note that I have here is that when you're brought on, clients they expect expertise in a technology, something like Ruby on Rails or, you know, just the web environment in general. They don't expect you as a consultant to be an expert in their domain or their practices. And so, when you really engage with this sort of areas that are new by asking a lot of questions, that's the thing that's really valuable, especially if those questions are coming from a place of experience in other similar things. So, maybe asking some questions around testing strategies because you've seen three or four other ways that work or don't work or that have different trade-offs. Even asking about, "Hey, I see we went down a particular path, technically. Can you walk me through what were the trade-offs that we evaluated and why we decided this was the path that was valuable for us?" That's something that people really appreciate from outside experts. Because it shows that you've got experience in those trade-offs, that you've thought the deeper thoughts beyond just shipping the next ticket. And sometimes they've made the decisions without actually thinking through the trade-offs. And so, that can be an opening for a conversation of like, "Hey, well, we just went down this path because we saw a blog article that recommended this, or we just did this because it felt right. Talk us through the trade-offs." And now maybe you have a conversation on, "Hey, here are the trade-offs that you're doing. Let me know if this sounds right for your organization. If not, maybe you want to consider changing some things or tweaking your approach." And I think that is valuable sort of at the big level where you're thinking about how the team is structured, how different parts of work is done, the technical architecture, but it also is valuable at the small level as well. STEPHANIE: Yeah, 100%. There is a blog post I really like by Hazel Weakly, and it's called "The Power of Being New: A Proven Recipe for High Impact." And one thing that she says at the beginning that I really enjoy is that even though, like, whenever you start on a new team there's always that little bit of pressure of starting to deliver immediate value, right? But there's something really special about that period where no one expects you to do anything, like, super useful immediately [laughs]. And I feel like it is both a fleeting time and, you know, I'm excited to continue this conversation of, like, how to keep integrating that even after you're no longer new. But I like to use that time to just identify, while I have nothing really on my plate, like, things that might have just been overlooked or just people have gotten used to that sometimes is, honestly, like, can be a quick fix, right? Like, just, I don't know, deleting a piece of dead code that you're seeing is no longer used but just gets fallen off other people's plates. I really enjoy those first few weeks, and people are almost, like, always so appreciative, right? They're like, "Oh my gosh, I have been meaning to do that." Or like, "Great find." And these are things that, like I said, just get overlooked when you are, yeah, kind of busy with other things that now are your responsibility. JOËL: You're talking about, like, that feeling of can you add value in the, like, initial time that you join. And I think that sometimes it can be easy to think that, oh, the only value you can add is by, like, shipping code. I think that being sort of noisy and asking a lot of questions in Slack is often a great way to add value, especially at first. STEPHANIE: Yeah, agreed. JOËL: Ideally, I think you come in, and you don't sort of slide in under the radar as, like, a new person on the team. Like, you come in, and everybody knows you're there because you are, like, spamming the channel with questions on all sorts of things and getting people to either link you to resources they have or explaining different topics, especially anything domain-related. You know, you're coming in with an outside expertise in a technology. You are a complete new person at the business and the problem domain. And so, that's an area where you need to ask a lot of questions and ramp up quickly. STEPHANIE: Yes. I have a kind of side topic. I guess it's not a side topic. It's about asking questions, so it's relevant [laughs]. But one thing that I'm curious about is how do you approach kind of doing this in a place where question asking is not normalized and maybe other people are less comfortable with kind of people asking questions openly and in public? Like, how do you set yourself up to be able to ask questions in a way that doesn't lead to just, like, some just, like, suspicion or discomfort about, like, why you're asking those questions? JOËL: I think that's the beauty of the consultant title. When an organization brings in outside experts, they kind of expect you to ask questions. Or maybe it's not an explicit expectation, but when they see you asking a lot of questions, it sort of, I think, validates a lot of things that they expect about what an outside expert should be. So, asking a lot of questions of trying to understand your business, asking a lot of questions to try to understand the technical architecture, asking questions around, like, some subtle edge cases or trade-offs that were made in the technical architecture. These are all things that help clients feel like they're getting value for the money from an outside expert because that's what you want an outside expert to do is to help you question some of your assumptions, to be able to leverage their, like, general expertise in a technology by applying it to your specific situation. I've had situations where I'll ask, like, a very nuanced, deep technical question about, like, "Hey, so there's, like, this one weird edge case that I think could potentially happen. How do we, like, think through about this?" And one of the, like, more senior people on the team who built the initial codebase responded, like, almost, like, proud that I've discovered this, like, weird edge case, and being like, "Oh yeah, that was a thing that we did think about, and here's why. And it's really cool that, like, day one you're, like, just while reading through the code and were like, 'Oh, this thing,' because it took us, like, a month of thinking about it before we stumbled across that." So, it was a weird kind of fun interaction where as a new person rolling on, one of the more experienced devs in the codebase almost felt, like, proud of me for having found that. STEPHANIE: I like that, yeah. I feel like a lot of the time...it's like, it's so easy to ask questions to help people feel seen, to be like, "Oh yeah, like, I noticed this." And, you know, if you withhold any kind of, like, judgment about it when you ask the question, people are so willing to be like, yeah, like you said, like, "Oh, I'm glad you saw that." Or like, "Isn't that weird? Like, I was feeling, you know, I saw that, too." Or, like, it opens it up, I think, for building trust, which, again, like, I don't even think this is something that you necessarily need to be new to even do. But if at any point you feel like, you know, maybe your working relationship with someone could be better, right? To the point where you feel like you're, like, really on the same page, yeah, ask questions [laughs]. It can be that easy. JOËL: And I think what can be really nice is, in an environment where question asking is not normalized, coming in and doing that can help sort of provide a little bit of cover to other people who are feeling less comfortable or less safe doing that. So, maybe there's a lot of junior members on the team who are feeling not super confident in themselves and are afraid that asking questions might undermine their position in the company. But me coming in as a sort of senior consultant and asking a lot of those questions can then help normalize that as a thing because then they can look and say, "Oh, well he's asking all these questions. Maybe I can ask my question, and it'll be okay." STEPHANIE: I also wanted to talk about setting yourself up and asking questions to get a good answer, asking good questions to get useful answers. One thing that has worked really well for me in the past few months has been sharing why I'm asking the question. And I think this goes back to a little bit of what I was hinting at earlier. If the culture is not really used to people asking questions and that just being a thing that is normal, sharing a bit of intention can help, like, ease maybe some nervousness that people might feel. Especially as consultants, we also are in a bit of a, I don't know, like, there is some power dynamics occasionally where it's like, oh, like, the consultants are here. Like, what are they going to come in and change or, like, start, you know, doing to, quote, unquote, "improve", whatever, I don't know [laughs]. JOËL: Right, right. STEPHANIE: Yeah, that's the consultant archetype, I think. Anyway. JOËL: Just coming in and being like, "Oh, this is bad, and this is bad, and you're doing it wrong." STEPHANIE: [laughs] JOËL: Ooh, I would be ashamed if I was the author of this code. STEPHANIE: Yeah, my hot take is that that is a bad consultant [laughs]. But maybe I'll say, like, "I am looking for some examples of this pattern. Where can I find them [laughs]?" Or "I've noticed that the team is struggling with, like, this particular part of the codebase, and I am thinking about improving it. What are some of your biggest challenges, like, working with this, like, model?" something like that. And I think this also goes back to, like, proving value, right? Even if it's like, sometimes I know kind of what I want to do, and I'll try to be explicit about that. But even before I have, like, a clear action item, I might just say like, "I'm thinking about this," you know, to convey that, you know, I'm still in that information gathering stage, but the result of that will be useful to help me with whatever kind of comes out of it. JOËL: A lot of it is about, like, genuine curiosity and an amount of empathetic listening. Existing team knows a lot about both the code and the business. And as a consultant coming on or maybe even a more senior person onboarding onto a team, the existing team has so much that they can give you to help you be better at your job. STEPHANIE: I was also revisiting a really great blog post from Julia Evans about "How to Ask Good Questions." And this one is more geared towards asking technical questions that have, like, kind of a maybe more straightforward answer. But she included a few other strategies that I liked a lot. And, frankly, I feel like I want to be even better at finding the right time to ask questions [laughs] and finding the right person to ask those questions to. I definitely get in the habit of just kind of like, I don't know, I'll just put it out there and [laughs], hopefully, get some answers. But there are definitely ways, I think, that you can be more strategic, right? About identifying who might be the best person to provide the answers you're looking for. And I think another thing that I often have to balance in the consulting position is when to know when to, like, stop kind of asking the really big questions because we just don't have time [laughs]. JOËL: Right. You don't want to be asking questions in a way that's sort of undermining the product, or the decisions that are being made, or the work that has to get done. Ideally, the questions that you're asking are helping move the project forward in a positive way. Nobody likes the, you know, just asking kind of person. That person's annoying. STEPHANIE: Do you have an approach or any thoughts about like, once you get an answer, like, what do you do with that? Yeah, what happens then for you? JOËL: I guess there's a lot of different ways it can go. A potential way if it's just, like, an answer explained in Slack, is maybe saying, "We should document this." Or maybe even like, "Is this documented anywhere? If not, can I add that documentation somewhere?" And maybe that's, you know, a code comment that we want to add. Maybe that's an entry to the Wiki. Maybe that's updating the README. Maybe that's adding a test case. But converting that into something actionable can often be a really good follow-up. STEPHANIE: Yeah, I think that mitigates the just asking [laughs] thing that you were saying earlier, where it's like, you know, the goal isn't to ask questions to then make more work for other people, right? It's to ask questions so, hopefully, you're able to take that information and do something valuable with it. JOËL: Right. Sometimes it can be a sort of setup for follow-up questions. You get some information and you're like, okay, so, it looks like we do have a pattern for interacting with third-party APIs, but we're not using it consistently. Tell me a little bit about why that is. Is that a new pattern that we've introduced and we're trying to, like, get more buy-in from the team? Is this a pattern that we used to have, and we found out we didn't like it? So, we stopped using it, but we haven't found a replacement pattern that we like. And so, now we're just kind of...it's a free-for-all, and we're trying to figure it out. Maybe there's two competing patterns, and there is this, like, weird politics within the tech team where they're sort of using one or the other, and that's something I'm going to have to be careful to navigate. So, asking some of those follow-up questions and once you have a technical answer can yield a lot of really interesting information and then help you think about how you can be impactful on the organization. STEPHANIE: And that sounds like advice that's just true, you know, regardless of your role or how long you've been in it, don't you think? JOËL: I would say yes. If you've been in the role a long time, though, you're the person who has that sort of institutional history in your mind. You know that in 2022, we switched over from one framework to another. You know that we used to have this, like, very opinionated architect who mandated a particular pattern, and then we moved away from it. You know that we were all in on this big feature last summer that we released and then nobody used it, and then the business pivoted, but there's still aspects of it that are left around. Those are things that someone knew onboarding doesn't know and that, hopefully, they're asking questions that you can then answer. STEPHANIE: Have you been in the position where you have all that, like, institutional knowledge? And then, like, how do you maintain that sense of curiosity or just that sense of kind of, like, what you're talking about, that superpower that you get when you're new of being able to just, you know, kind of question why things are the way they are? JOËL: It's hard, right? We're talking about how do you keep that sort of almost like a beginner's mindset, in this case, maybe less of a, like, new coder mindset and more of a new hire mindset. It's something that I think is much more front of mind for me because I rotate onto new clients every, like, 6 to 12 months. And so, I don't have very long to get comfortable before I'm immediately thrown into, like, a new situation. But something that I like to do is to never sort of solely be in one role or the other, a sort of, like, experienced person helping others or the new person asking for help. Likely, you are not going to be the newest person on the team for long. Maybe you came on as a cohort and you've got a group of new people, all of whom are asking different questions. And maybe somebody is asking a question that you've asked before, that you've asked in a different channel or on a call with someone. Or maybe someone joins two weeks after you; you don't have deep institutional knowledge. But if you've been asking a lot of questions, you've been building a lot of that for yourself, and you have a little bit that you can share to the next person who knows even less than you do. And that's an approach that I took even as an apprentice developer. When I was, like, brand new to Rails and I was doing an internship, and another intern joined me a couple of weeks after, and I was like, "You know what? I barely know anything. But I know what an instance variable is. And I can help you write a controller action. Let's pair on that. We'll figure it out. And, you know, ask me another question next week. I might have more answers for you." So, I guess a little bit of paying it forward. STEPHANIE: Yeah, I really like that advice, though, of, like, switching up the role or, like, kind of what you're working on, just finding opportunities to practice that, you know, even if you have been somewhere for a long time. I think that is really interesting advice. And it's hard, too, right? Because that requires, like, doing something new, and doing something new can be hard [laughs]. But if you're, you know, aren't in a consultant role, where you're not rotating onto new projects every 6 to 12 months, that, I feel like, would be a good strategy to grow in that particular way. JOËL: And even if you're not switching companies or in a consulting situation, it's not uncommon to have people switch from one team to another within an organization. And new team might mean new dynamics. That team might be doing a slightly different approach to project management. Their part of the code might be structured slightly differently. They might be dealing with a part of the business domain that you're less familiar with. While that might not be entirely new to you because, you know, you know a little bit of the organization's DNA and you understand the organization's mission and their core product, there are definitely a lot of things that will be new to you, and asking those questions becomes important. STEPHANIE: I also have another kind of, I don't know, it's not even a strategy. It's just a funny thing that I do where, like, my memory is so poor that, like, even code I wrote, you know, a month ago, I'm like, oh, what was past Stephanie thinking here [laughs]? You know, questioning myself a little bit, right? And being willing to do that and recognizing that, like, I have information now that I didn't have in the past. And, like, can that be useful somehow? You know, it's like, the code I wrote a month ago is not set in stone. And I think that's one way I almost, like, practice that skill with myself [laughs]. And yeah, it has helped me combat that, like, things are the way they are mentality, which, generally, I think is a very big blocker [laughs] when it comes to software development, but that's a topic for another day [laughs]. JOËL: I like the idea of questioning yourself, and I think that's something that is a really valuable skill for all developers. I think it can come up in things like documentation. Let's say you're leaving a comment on a method, especially one that's a bit weird, being able to answer that "Why was this weird technical decision made?" Or maybe you do this in your PR description, or your commit message, or in any of the other places where you do this, not just sort of shipping the code as is, but trying to look at it from an outsider's eyes. And being like, what are the areas where they're going to, like, get a quizzical look and be like, "Why is this happening? Why did you make this choice?" Bonus points if you talked a little bit about the trade-offs that were decided on to say, "Hey, there were two different implementations available for this. I chose to take implementation A because I like this set of trade-offs better." That's gold. And, I guess, as a reviewer, if I'm seeing that in a PR, that's going to make my job a lot easier. STEPHANIE: Yes. Yeah, I never thought about it that way, but yeah, I guess I do kind of apply, you know, the things that I would kind of ask to other team members to myself sometimes. And that is...it's cool to hear that you really appreciate that because I always kind of just did it for myself [laughs], but yeah, I'm sure that it, like, is helpful for other people as well. JOËL: I guess you were asking what are ways that you can ask questions even when you are more established. And talking about these sorts of self-reflective questions in the context of review got me thinking that PRs are a great place to ask questions. They're great when you're a newcomer. One of the things I like to do when I'm new on a project is do a lot of PR reviews so I can just see the weird things that people are working on and ask a lot of questions about the patterns. STEPHANIE: Yep. Same here. JOËL: Do a lot of code reading. But that's a thing that you can keep doing and asking a lot of questions on PRs and not in a, like, trying to undermine what the person is doing, but, like, genuine questions, I think, is a great way to maintain that mindset. STEPHANIE: Yeah, yeah, agreed. And I think when I've seen it done well, it's like, you get to be engaged and involved with the rest of your team, right? And you kind of have a bit of an idea about what people are working on. But you're also kind of entrusting them with ownership of that work. Like, you don't need to be totally in the weeds and know exactly how every method works. But, you know, you can be curious about like, "Oh, like, what were you thinking about this?" Or like, "What about this pattern appeals to you?" And all of that information, I think, helps you become a better, like, especially a senior developer, but also just, like, a leader on the team, I think. JOËL: Yeah, especially the questions around like, "Oh, walk me through some of the trade-offs that you chose for this method." And, you know, for maybe a person who's more senior, that's great. They have an opportunity to, like, talk about the decisions they made and why. That's really useful information. For a more junior person, maybe they've never thought about it. They're like, "Oh, wait, there are trade-offs here?" and now that's a great learning opportunity for them. And you don't want to come at it from a place of judgment of like, oh, well, clearly, you know, you're a terrible developer because you didn't think about the performance implications of this method. But if you come at it from a place of, like, genuine curiosity and sort of assuming the best of people on the team and being willing to work alongside them, help them discover some new concepts...maybe they've never, like, interacted so much with performance trade-offs, and now you get to have a conversation. And they've learned a thing, and everybody wins. STEPHANIE: Yeah. And also, I think seeing people ask questions that way helps more junior folks also learn when to ask those kinds of questions, even if they don't know the answer, right? But maybe they start kind of pattern matching. Like, oh, like, there might be some other trade-offs to consider with this kind of code, but I don't know what they are yet. But now I know to at least start asking and find someone who can help me determine that. And when I've seen that, that has been always, like, just so cool because it's upskilling happening [laughs] in practice. JOËL: Exactly. I love that phrase that you said: "Asking questions where you don't know the answers," which I think is the opposite of what lawyers are taught to do. I think lawyers the mantra they have is you never ask a witness a question that you don't know the answer to. But I like to flip that for developers. Ask a lot of questions on PRs where you don't know the answer, and you'll grow, and the author will grow. And this is true across experience levels. STEPHANIE: That's one of my favorite parts about being a developer, and maybe that's why I will never be a lawyer [laughter]. JOËL: On that note, I have a question maybe I do know the answer to. Shall we wrap up? STEPHANIE: Let's wrap up. Show notes for this episode can be found at bikeshed.fm. JOËL: This show has been produced and edited by Mandy Moore. STEPHANIE: If you enjoyed listening, one really easy way to support the show is to leave us a quick rating or even a review in iTunes. It really helps other folks find the show. JOËL: If you have any feedback for this or any of our other episodes, you can reach us @_bikeshed, or you can reach me @joelquen on Twitter. STEPHANIE: Or reach both of us at hosts@bikeshed.fm via email. JOËL: Thanks so much for listening to The Bike Shed, and we'll see you next week. ALL: Byeeeeeeee!!!!!!! AD: Did you know thoughtbot has a referral program? If you introduce us to someone looking for a design or development partner, we will compensate you if they decide to work with us. More info on our website at: tbot.io/referral. Or you can email us at: referrals@thoughtbot.com with any questions.

    430: Test Suite Pain & Anti-Patterns

    Play Episode Listen Later Jun 25, 2024 40:57


    Stephanie and Joël discuss the recent announcement of the call for proposals for RubyConf in November. Joël is working on his proposals and encouraging his colleagues at thoughtbot to participate, while Stephanie is excited about the conference being held in her hometown of Chicago! The conversation shifts to Stephanie's recent work, including completing a significant client project and her upcoming two-week refactoring assignment. She shares her enthusiasm for refactoring code to improve its structure and stability, even when it's not her own. Joël and Stephanie also discuss the everyday challenges of maintaining a test suite, such as slowness, flakiness, and excessive database requests. They discuss strategies to balance the test pyramid and adequately test critical paths. Finally, Joël emphasizes the importance of separating side effects from business logic to enhance testability and reduce complexity, and Stephanie highlights the need to address testing pain points and ensure tests add real value to the codebase. RubyConf CFP (https://sessionize.com/rubyconf-2024/) RubyConf CFP coaching (https://docs.google.com/forms/d/e/1FAIpQLScZxDFaHZg8ncQaOiq5tjX0IXvYmQrTfjzpKaM_Bnj5HHaNdw/viewform?pli=1) Testing pyramid (https://thoughtbot.com/blog/rails-test-types-and-the-testing-pyramid) Outside-in testing (https://thoughtbot.com/blog/testing-from-the-outsidein) Writing fewer system specs with request specs (https://thoughtbot.com/blog/faster-tests-with-capybara-and-request-specs) Unnecessary factories (https://thoughtbot.com/blog/speed-up-tests-by-selectively-avoiding-factory-bot) Your Test Suite is Making Too Many Database Calls (https://www.youtube.com/watch?v=LOlG4kqfwcg) Your flaky tests might be time dependent (https://thoughtbot.com/blog/your-flaky-tests-might-be-time-dependent) The Secret Ingredient: How To Understand and Resolve Just About Any Flaky Test (https://www.youtube.com/watch?v=De3-v54jrQo) Separating side effects to improve tests (https://thoughtbot.com/blog/simplify-tests-by-extracting-side-effects) Functional core, imperative shell (https://www.destroyallsoftware.com/screencasts/catalog/functional-core-imperative-shell) Thoughtbot testing articles (https://thoughtbot.com/blog/tags/testing) Transcript: STEPHANIE: Hello and welcome to another episode of The Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I'm Stephanie Minn. JOËL: And I'm Joël Quenneville. And together, we're here to share a bit of what we've learned along the way. STEPHANIE: So, Joël, what's new in your world? JOËL: Something that's new in my world is that RubyConf just announced their call for proposals for RubyConf in November. They're open for...we're currently recording in June, and it's open through early July, and they're asking people everywhere to submit talk ideas. I have a few of my own that I'm working with. And then, I'm also trying to mobilize a lot of other colleagues at thoughtbot to get excited to submit. STEPHANIE: Yes, I am personally very excited about this year's RubyConf in November because it's in Chicago, where I live, so I have very little of an excuse not to go [laughs]. I feel like so much of my conference experience is traveling to just kind of, like, other cities in the U.S. that I want to spend some time in and, you know, seeing all of my friends from...my long-distance friends. And it definitely does feel like just a bit of an immersive week, right? And so, I wonder how weird it will feel to be going to this conference and then going home at the end of the night. Yeah, that's just something that I'm a bit curious about. So, yeah, I mean, I am very excited. I hope everyone comes to Chicago. It's a great city. JOËL: I think the pitch that I'm hearing is submit a proposal to the RubyConf CFP to get a chance to get a free ticket to go to RubyConf, where you get to meet Bike Shed co-host Stephanie Minn. STEPHANIE: Yes. Ruby Central should hire me to market this conference [laughter] and that being the main value add of going [laughs], obviously. Jokes aside, I'm excited for you to be doing this initiative again because it was so successful for RailsConf kind of internally at thoughtbot. I think a lot of people submitted proposals for the first time with some of the programming you put on. Are you thinking about doing things any differently from last time, or any new thoughts about this conference cycle? JOËL: I think I'm iterating on what we did last time but trying to keep more or less the same formula. Among other things, people don't always have ideas immediately of what they want to speak about. And so, I have a brainstorming session where we're just going to get together and brainstorm a bunch of topics that are free for anyone to take. And then, either someone can grab one of those topics and pitch a talk on it, or it can be, like, inspiration where they see that it jogs their mind, and they have an idea that then they go off and write a proposal. And so, that allows, I think, a lot of colleagues as well, who are maybe not interested in speaking but might have a lot of great ideas, to participate and sort of really get a lot of that energy going. And then, from there, people who are excited to speak about something can go on to maybe draft a proposal. And then, I've got a couple of other events where we support people in drafting a proposal and reviewing and submitting, things like that. STEPHANIE: Yes, I really love how you're just involving people with, you know, just different skills and interests to be able to support each other, even if, you know, there's someone on our team who's, like, not interested in speaking at all, but they're, like, an ideas person, right? And they would love to see their idea come to life in a talk from someone else. Like, I think that's really cool, and I certainly appreciate it as a not ideas person [laughs]. JOËL: Also, I want to shout out that Ruby Central is doing CFP coaching sessions on June 24th, June 25th, and June 26th, and those are open to anyone. You can sign up. We'll put a link to the signup form in the show notes. If you've never submitted something before and you'd like some tips on what makes for a good CFP, how can you up your chances of getting accepted, or maybe you've submitted before, you just want to get better at it; I recommend joining one of those slots. So, Stephanie, what's new in your world? STEPHANIE: So, I just successfully delivered a big project on my client work last week. So, I'm kind of riding that wave and getting into the next bit of work that I have been assigned for this team, and I'm really excited to do this. But I also, I don't know, I've been just, like, thinking about it quite a bit. Basically, I'm getting to spend two dedicated weeks to just refactoring [laughs] some really, I guess, complicated code that has led to some bugs recently and just needing some love, especially because there's some whiffs of potentially, like, really investing in this area of the product, and people wanting to make sure that the foundation does feel very stable to build on top of for extending and changing that code. And I think I, like, surprised myself by how excited I was to do this because it's not even code I wrote. You know, sometimes when you are the one who wrote code, you're like, oh, like, I would love time to just go back and clean up all these things that I kind of missed the first time around or just couldn't attend to for whatever reason. But yeah, I think I was just a little bit in the peripheries of that code, and I was like, oh, like, just seeing some weird stuff. And now to kind of have the time to be like, oh, this is all I'm going to be doing for two weeks, to, like, really dive into it and get my hands dirty [laughs], I'm very excited. JOËL: I think that refactoring is a thing that can be really fun. And also, when you have a larger chunk of time, like two days, it's easy to sort of get lost in sort of grand visions or projects. How do you kind of balance the, I want to do a lot of refactoring; I want to take on some bigger things while maybe trying to keep some focus or have some prioritization? STEPHANIE: Yeah, that's a great question. I was actually the one who said, like, "I want two weeks on this." And it also helped that, like, there was already some thoughts about, like, where they wanted to go with this area of the codebase and maybe what future features they were thinking about. And there are also a few bugs that I am fixing kind of related to this domain. So, I think that is actually what I started with. And that was really helpful in just kind of orienting myself in, like, the higher impact areas and the places that the pain is felt and exploring there first to, like, get a sense of what is going on here. Because I think that information gathering is really important to be able to kind of start changing the code towards what it wants to be and what other devs want it to be. I actually also started a thread in Slack for my team. I was, like, asking for input on what's the most confusing or, like, hard to reason about files or areas in this particular domain or feature set and got a lot of really good engagement. I was pleasantly surprised [laughs], you know, because sometimes you, like, ask for feedback and just crickets. But I think, for me, it was very affirming that I was, like, exploring something that a lot of people are like, oh, we would love for someone to, you know, have just time to get into this. And they all were really excited for me, too. So, that was pretty cool. JOËL: Interesting. So, it sounds like you sort of budgeted some refactoring time and then, from there, broke it down into a series of a couple of debugging projects and then a couple of, like, more bounded refactoring projects, where, like, specifically, I want to restructure the way this object works or something like that. STEPHANIE: Yeah. I think there was that feeling of wanting to clean up this area of the codebase, but you kind of caught on to that bit of, you know, it can go so many different ways. And, like, how do you balance your grand visions [laughs] of things with, I guess, a little bit of pragmatism? So, it was very much like, here's all these bugs that are causing our customers problems that are kind of, like, hard for the devs to troubleshoot. You know, that kind of prompts the question, like, why? And so, if there can be, you know, the fixing of the bugs, and then the learning of, like, how that part of the system works, and then, hopefully, some improvements along the way, yeah, that just felt like a dream [laughs] for me. And two weeks felt about the right amount of time. I don't know if anyone kind of hears that and feels like it's too long or too little. I would be really curious. But I feel like it is complex enough that, like, context switching would, I think, make this work harder, and you kind of do have to just sit with it for a little bit to get your bearings. JOËL: A scenario that we encounter on a pretty regular basis is a customer coming to us and telling us that they're feeling a lot of test pain and asking what are the ways that we can help them to make things better and that test pain can come under a lot of forms. It might be a test suite that's really slow and that's hurting the team in terms of their velocity. It might be a test suite that is really flaky. It might be one that is really difficult to add to, or maybe one that has very low coverage, or one that is just really brittle. Anytime you try to make a change to it, a bunch of things break, and it takes forever to ship anything. So, there's a lot of different aspects of challenging test suites that clients come to us with. I'm curious, Stephanie, what are some of the ones that you've encountered most frequently? STEPHANIE: I definitely think that a slow test suite and a flaky test suite end up going hand in hand a lot, or just a brittle one, right? That is slowing down development and, like you said, causing a lot of pain. I think even if that's not something that a client is coming to us directly about, it maybe gets, like, surfaced a little bit, you know, sometime into the engagement as something that I like to keep an eye on as a consultant. And I actually think, yeah, that's one of kind of the coolest things, I think, about our consulting work is just getting to see so many different test suites [laughs]. I don't know. I'm a testing nerd, so I love that kind of stuff. And then, I think you were also kind of touching on this idea of, like, maintaining a test suite and, yeah, making testing just a better experience. I have a theory [laughs], and I'd be curious to get your thoughts on it. But one thing that I really struggle with in the industry is when people talk about writing tests as if it's, like, the morally superior thing to do. And I struggle with this because I don't think that it is a very good strategy for helping people feel better or more confident and, like, upskill at writing tests. I think it kind of shames people a little bit who maybe either just haven't gotten that experience or, you know, just like, yeah, like, for whatever reason, are still learning how to do this thing. And then, I think that mindset leads to bad tests [laughs] or tests that aren't really serving the purpose that you hope they would because people are doing it more out of obligation rather than because they truly, like, feel like it adds something to their work. Okay, I kind of just dropped that on you [laughs]. Do you have any reactions? JOËL: Yeah, I guess the idea that you're just checking a box with your test rather than writing code that adds value to the codebase. They're two very different perspectives that, in the end, will generate more lines of code if you're just doing a checkbox but may or may not add a whole lot of value. So, maybe before even looking at actual, like, test practices, it's worth stepping back and asking more of a mindset question: Why does your team test? What is the value that your team feels they get out of testing? STEPHANIE: Yeah. Yeah. I like that because I was about to say they go hand in hand. But I do think that maybe there is some, you know, question asking [laughs] to be done because I do think people like to kind of talk about the testing practices before they've really considered that. And I am, like, pretty certain from just kind of, at least what I've seen, and what I've heard, and what I've experienced on embedding into client teams, that if your team can't answer that question of, like, "What value does testing bring?" then they probably aren't following good testing practices [laughs]. Because I do think you kind of need to approach it from a perspective of like, okay, like, I want to do this because it helps me, and it helps my team, rather than, like you said, getting the check mark. JOËL: So, once we've sort of established maybe a bit of a mindset or we've had a conversation with the team around what value they think they're getting out of tests, or maybe even you might need to sell the team a little bit on like, "Hey, here's, like, all these different ways that testing can bring value into your life, make your life as developers easier," but once you've done that sort of pre-work and you can start looking at what's actually the problem with a test suite, a common complaint from developers is that the test suite is too slow. How do you like to approach a slow test suite? STEPHANIE: That's a good question. I actually...I think there's a lot of ways to answer that. But to kind of stay on the theme of stepping back a little bit, I wonder if assessing how well your test suite aligns with the testing pyramid would be a good place to start; at least, that could be where I start if I'm coming into a client team for the first time, right, and being asked to start assessing or just poking around. Because I think the slowness a lot of the time comes from a lot of quote, unquote, "integration tests" or, like, unit tests masquerading as integration tests, where you end up having, like, a lot of duplication of things that are being tested in ways that are integrating with some slow parts of the system like the database. And yeah, I think even before getting into some of the more discreet reasons why you might be writing slow tests, just looking at the structure of your test suite and what kinds of things you're testing, and, again, even going back to your team and asking, like, "What kinds of things do you test?" Or like, "Do you try to test or wish to be testing more of, less of?" Like looking at the structure, I have found to be a good place to start. JOËL: And for those who are not familiar, you used the term testing pyramid. This is a concept which says that you probably want to have a lot of small, fast unit tests, a medium amount of integration tests that test a few different components together, and then a few end-to-end tests. Because as you go up that pyramid, tests become more expensive. They take a lot longer to run, whereas the little unit tests are super cheap. You can create thousands of them, and they will barely impact your run time. Adding a dozen end-to-end tests is going to be noticeable. So, you want to balance sort of the coverage that you get from end to end with the sort of cheapness and ubiquity of the little unit tests, and then split the difference for tests that are in between. STEPHANIE: And I think that is challenging, even, you know, you're talking about how you want the peak of your pyramid to be end-to-end tests. So, you don't want a lot of them, but you do want some of them to really ensure that things are totally plumbed and working correctly. But that does require, I think, really looking at your application and kind of identifying what features are the most critical to it. And I think that doesn't get paid enough attention, at least from a lot of my client experiences. Like, sometimes teams just end up with a lot of feature bloat and can't say like, you know, they say, "Everything's important [chuckles]," but everything can't be equally important, you know? JOËL: Right. I often like to develop using a sort of outside-in approach, where you start by writing an end-to-end test that describes the behavior that your new feature ticket is asking for and use that to drive the work that I'm doing. And that might lead to some lower-level unit tests as I'm building out different components, but the sort of high-level behavior that we're adding is driven by adding an end-to-end spec. Do you feel that having one new end-to-end spec for every new feature ticket that you work on is a reasonable thing to do, or do you kind of pick and choose? Do you write some, but maybe start, like, coalescing or culling them, or something like that? How do you manage that idea that maybe you would or would not want one end-to-end spec for each feature ticket? STEPHANIE: Yeah, it's a good question. Actually, as you were saying that, I was about to ask you, do you delete some afterwards [laughs]? Because I think that might be what I do sometimes, especially if I'm testing, you know, edge cases or writing, like, the end-to-end test for error states. Sometimes, not all of them make it into my, like, final, you know, commit. But they, you know, had their value, right? And at least it prompted me to make sure I thought about them and make sure that they were good error states, right? Like things that had visible UI to the user about what was going on in case of an error. So, I would say I will go back and kind of coalesce some of them, but they at least give me a place to start. Does that match your experience? JOËL: Yeah, I tend to mostly write end-to-end tests for happy paths and then write kind of mid-level things to cover some of my edge cases, maybe a couple of end-to-end tests for particularly critical paths. But, at some point, there's just too many paths through the app that you can't have end-to-end coverage for every single branch on every single path that can happen. STEPHANIE: Yeah, I like that because if you find yourself having a lot of different conditions that you need to test in an end-to-end situation, maybe there's room for that to, like, be better encapsulated in that, like, more, like, middle layer or, I don't know, even starting to ask questions about, like, does this make sense with the product? Like, having all of these different things going on, does that line up with kind of the vision of what this feature is trying to be or should be? Because I do think the complexity can start at that high of a level. JOËL: How do you feel about the idea that adding more end-to-end tests, at some point, has diminishing returns? STEPHANIE: I'm not quite sure I'm following [laughs]. JOËL: So, let's say you have an end-to-end test for the happy path for every core feature of the app. And you decide, you know what, I want to add maybe some, like, side features in, or maybe I want to have more error states. And you start, like, filling in more end-to-end tests for those. Is it fair to say that adding some of those is a bit of a diminishing return? Like, you're not getting as much value as you would from the original specs. And maybe as you keep finding more and more rare edge cases, you get less and less value for your test. STEPHANIE: Oh, yeah, I see. And there's more of a cost, too, right? The cost of the time to run, maintain, whatever. JOËL: Right. Let's say they're roughly all equally expensive in terms of cost to run. But as you stray further and further off of that happy path, you're getting less and less value from that integration test or that end-to-end test. STEPHANIE: I'm actually a little conflicted about this because that sounds right in theory, but then in practice, I feel like I've seen error states not get enough love [laughs] that it's...I don't even want to say, like, you make any kind of claim [laughs] about it. But, you know, if you're going to start somewhere, if you have, like, a limited amount of time and you're like, okay, I'm only going to write a handful of end-to-end tests, yeah, like, write tests for your happy paths [laughs]. JOËL: I guess it's probably fair to say that error states just don't get as much love as they should throughout the entire testing stack: at the unit level, at the integration level, all the way up to end to end. STEPHANIE: I'm curious if you were trying to get at some kind of conclusion, though, with the idea of diminishing returns. JOËL: I guess I'm wondering if, from there, we can talk about maybe a breakdown of a particular testing pyramid for a particular test suite is being top heavy, and whether there's value in maybe pushing some of these tests, some of these edge cases, some of these maybe less important features down from that, like, top end-to-end layer into maybe more of an integration layer. So, in a Rails context, that might be moving system specs down to something like a request spec. STEPHANIE: Yeah, I think that is what I tend to do. I'm trying to think of how I get there, and I'm not quite sure that I can explain it quite yet. Yeah, I don't know. Do you think you can help me out here? Like, how do you know it's time to start writing more tests for your unhappy paths lower on the pyramid? JOËL: Ideally, I think a lot of your code should be unit-tested. And when you are unit testing it, those pieces all need coverage of the happy and unhappy paths. I think the way it may often happen naturally is if you're pushing logic out of your controllers because it's a little bit challenging sometimes to test Rails controllers. And so, if you're moving things into domain objects, even service objects, depending on how you implement them, just doing that and then making sure you unit test them can give you a lot more coverage of all the different edge cases that can happen. Where things sometimes fall apart is getting out of that business layer into the web layer and saying, "Hey, if something raises an error or if the save fails or something like that, does the user get a good experience, or do we just crash and give them a 500 page?" STEPHANIE: Yeah, that matches with a lot of what I've seen, where if you then spend too much time in that business layer and only handling errors there, you don't really think too much about how it bubbles up. And, you know, then you are digging through, like, your error monitoring [laughs] service, trying to find out what happened so that you can tell, you know, your customer support team [laughs] to help them resolve, like, a bug report they got. But I actually think...and you were talking about outside in, but, in some ways, in my experience, I also get feedback from the bottom up sometimes that then ends up helping me adjust some of those integration or end-to-end tests about kind of what errors are possible, like, down in the depths of the code [laughs], and then finding ways to, you know, abstract that or, like, kind of be like, "Oh, like, here are all these possible, like, exceptions that might be raised." Like, what HTTP status code do I want to be returned to capture all of these things? And what do I want to say to the user? So, yeah, I'm [laughs] kind of a little lost myself, but this idea that going both, you know, outside in and then maybe even going back up a little bit has served me well before. JOËL: I think there can be a lot of value in sort of dropping down a level in the pyramid, and maybe instead of doing sort of end-to-end tests where you, like, trigger a scenario where something fails, you can just write a request back against the controller and say, "Hey, if I go to this controller and something raises an error, expect that you get redirected to this other location." And that's really cheap to run compared to an end-to-end test. And so, I think that, for me, is often the right compromise is handling error states at sort of the next lowest level and also in slightly more atomic pieces. So, more like, if you hit this endpoint and things go wrong, here's how things happen. And I use endpoint not so much in an API sense, although it could be, but just your, you know, maybe you've got a flow that's multiple steps where, you know, you can do a bunch of things. But I might have a test just for one controller action to say, "Hey, if things go wrong, it redirects you here, or it shows you this error page." Whereas the end-to-end test might say, "Oh, you're going to go through the entire flow that hits multiple different controllers, and the happy path is this nice chain." But each of the exit points off at where things fail would be covered by a more scoped request spec on that controller. STEPHANIE: Yeah. Yeah. That makes sense. I like that. JOËL: So, that's kind of how I've attempted to balance my pyramid in a way that balances complexity and time with coverage. You mentioned that another area that test suites get slow is making too many requests to the database. There's a lot of ways that that happens. Oftentimes, I think a classic is using a factory where you really don't need to, persisting data to the database when all you needed was some object in memory. So, there are different strategies for avoiding that. It's also easy to be creating too much data. So, maybe you do need to persist some things in the database, but you're persisting a hundred objects into memory or into the database when you really meant to persist two, so that's an easy accident. A couple of years ago, I gave a talk at RailsConf titled "Your Test Suite is Making Too Many Database Requests" that went over a bunch of different ways that you can be doing a lot of expensive database requests you didn't plan on making and how that slows down your test suite. So, that is also another hot spot that I like to look at when dealing with a slow test suite. STEPHANIE: Yeah, I mentioned earlier the idea of unit tests really masquerading as integration tests [laughs]. And I think that happens especially if you're starting with a class that may already be a little bit too big than it should be or have more responsibilities than it should be. And then, you are, like, either just, like, starting with using the create build, like, strategy with factories, or you find yourself, like, not being able to fully run the code path you're trying to test without having stuff persisted. Those are all, I think, like, test smells that, you know, are signaling a little bit of a testing anti-pattern that, yeah, like, is there a way to write, like, true unit tests for this stuff where you are only using objects in memory? And does that require breaking out some responsibilities? That is a lot of what I am kind of going through right now, actually, with my little refractoring project [laughs] is backfilling some tests, finding that I have to create a lot of records. And you know what? Like, the first step will probably be to write those tests and commit them, and just have them live there for a little while while I figure out, you know, the right places to start breaking things up, and that's okay. But yeah, I did want to, like, just mention that if you are having to create a lot of records and then also noticing, like, your test is running kind of slow [laughs], that could be a good indicator to just give a good, hard look at what kind of style of test you think you're writing [laughs]. JOËL: Yeah, your tests speak to you, and when you're feeling pain, oftentimes, it can be a sign that you should consider refactoring your implementation. And I think that's doubly true if you're writing tests after the fact rather than test driving. Because sometimes you sort of...you came up with an implementation that you thought would be good, and then you're writing tests for it, and it's really painful. And that might be telling you something about the underlying implementation that you have that maybe it's...you thought it's well scoped, but maybe it actually has more responsibilities than you initially realized, or maybe it's just really tightly coupled in a way that you didn't realize. And so, learning to listen to your tests and not just sort of accepting the world for being the way it is, but being like, "No, I can make it better." STEPHANIE: Yeah, I've been really curious why people have a hard time, like, recognizing that pain sometimes, or maybe believing that this is the way it is and that there's not a whole lot that you can do about it. But it's not true, like, testing really does not have to be painful. And I feel like, again, this is one of those things that's like, it's hard to believe until you really experience it, at least, that was the case for me. But if you're having a hard time with tests, it's not because you're not smart enough. Like, that, I think, is a thing that I really want to debunk right now [laughs] for anyone who has ever had that thought cross their mind. Yeah, things are just complicated and complex somehow, or software entropy happens. That's, like, not how it should be, and we don't have to accept that [laughs]. So, I really like what you said about, oh, you can change it. And, you know, that is a bit of a callback to the whole mindset of testing that we mentioned earlier at the beginning. JOËL: Speaking of test suites, we have not covered yet is paralyzing it. That could probably be its own Bike Shed episode on its own entirely on paralyzing a test suite. We've done entire engagements where our job was to come in and help paralyze a test suite, make it faster. And there's a lot of, like, pros and cons. So, I think maybe we can save that for a different episode. And, instead, I'd like to quickly jump in a little bit to some other common pain points of test suites, and I would say probably top of that list is test flakiness. How do you tend to approach flakiness in a client project? STEPHANIE: I am, like, laughing to myself a little bit because I know that I was dealing with test flakiness on my last client engagement, and that was, like, such a huge part of my day-to-day is, like, hitting that retry button. And now that I am on a project with, like, relatively low flakiness, I just haven't thought about it at all [laughs], which is such a privilege, I think [laughs]. But one of the first things to do is just start, like, capturing metrics around it. If you, you know, are hearing about flakiness or seeing that, like, start to plague your test suite or just, you know, cropping up in different ways, I have found it really useful to start, like, I don't know, just, like, maybe putting some of that information in a dashboard and seeing how, just to, like, even make sure that you are making improvements, that things are changing, and seeing if there's any, like, patterns around what's causing the flakiness because there are so many different causes of it. And I think it is pretty important to figure out, like, what kind of code you're writing or just trying to wrangle. That's, you know, maybe more likely to crop up as flakiness for your particular domain or application. Yeah, I'm going to stop there and see, like, because I know you have a lot of thoughts about flakiness [laughs]. JOËL: I mean, you mentioned that there's a lot of different causes for flakiness. And I think, in my experience, they often sort of group into, let's say, like, three different buckets. Anytime you're testing code that's doing things that are non-deterministic, that's easy for tests to be flaky. And so, you might think, oh, well, you know, you have something that makes a call to random, and then you're going to assert on a particular outcome of that. Well, clearly, that's going to not be the same every time, and that might be flaky. But there are, like, more subtle versions of that, so maybe you're relying on the system clock in some way. And, you know, depending on the time you run that test, it might give you a different value than you expect, and that might cause it to fail. And it doesn't have to be you're asserting on, like, oh, specifically a given millisecond. You might be doing math around, like, number of days, and when you get near to, let's say, the daylight savings boundary, all of a sudden, no, you're off by an hour, and your number of days...calculation breaks because relying on the clock is something that is inherently non-deterministic. Non-determinism is a bucket. Leaky tests is another bucket of failures that I see, things where one test might impact another that gets run after the fact, oftentimes by mutating some sort of global state. So, maybe you're both relying on some sort of, like, external file that you're both writing to or maybe a cache that one is writing to that the other one is reading from, something like that. It could even just be writing records into the database in a way that's not wrapped in a transaction, such that there's more data in the database when the next test runs than it expects. And then, finally, if you are doing any form of parallelization, that can improve your test suite speed, but it also potentially leads to race conditions, where if your resources aren't entirely isolated between parallel test runners, maybe you're sharing a database, maybe you're sharing Redis instance or whatever, then you can run into situations where you're both kind of fighting over the same resources or overriding each other's data, or things like that, in a way that can cause tests to fail intermittently. And I think having a framework like that of categorization can then help you think about potential solutions because debugging approaches and then solutions tend to be a little bit different for each of these buckets. STEPHANIE: Yeah, the buckets of different causes of flaky tests you were talking about, I think, also reminded me that, you know, some flakiness is caused by, like, your testing environment and your infrastructure. And other kinds of flakiness are maybe caused more from just the way that you've decided how your code should work, especially that, like, non-deterministic bucket. So, yeah, I don't know, that was just, like, something that I noticed as you were going through the different categories. And yeah, like, certainly, the solutions for approaching each kind are very different. JOËL: I would like to pitch a talk from RubyConf last year called "The Secret Ingredient: How To Resolve And Understand Just About Any Flaky Test" by Alan Ridlehoover. Just really excellent walkthrough of these different buckets and common debugging and solving approaches to each of them. And I think having that framework in mind is just a great way to approach different types of flaky tests. STEPHANIE: Yes, I'll plus one that talk, lots of great pictures of delicious croissants as well. JOËL: Very flaky pastry. STEPHANIE: [laughs] Joël, do you have any last testing anti-pattern guidances for our audience who might be feeling some test pain out there? JOËL: A quick list, I'm going to say tight coupling that has then led to having a lot of stubbing in your tests often leads to tests that are very brittle, so tests that maybe don't fail when they should when you've actually broken things, or maybe, alternatively, tests that are constantly failing for the wrong reasons. And so, that is a thing that you can fix by making your code less coupled. Tests that also require stubbing a lot of things because you do a lot of side effects. If you are making a lot of HTTP calls or things like that, that can both make a test more complex because it has to be aware of that. But also, it can make it more non-deterministic, more flaky, and it can just make it harder to change. And so, I have found that separating side effects from sort of business logic is often a great way to make your test suite much easier to work with. I have a blog post on that that I'll link in the show notes. And I think this maybe also approaches the idea of a functional core and an imperative shell, which I believe was an idea pitched by Gary Bernhardt, like, over ten years ago. There's a famous video on that that we'll also link in the show notes. But that architecture for building an app can lead to a much nicer test to write. I guess the general idea being that testing code that does side effects is complicated and painful. Testing code that is more functional tends to be much more pleasant. And so, by not intermingling the two, you tend to get nicer tests that are easier to maintain. STEPHANIE: That's really interesting. I've not heard that guidance before, but now I am intrigued. That reminded me of another thing that I had a conversation with someone about. Because after the RailsConf talk I gave, which was about testing pain, there was some stubbing involved in the examples that I was showing because I just see a lot of that stuff. And, you know, this audience member kind of had that question of, like, "How do you know that things are working correctly if you have to stub all this stuff out?" And, you know, sometimes you just have to for the time being [chuckles]. And I wanted to just kind of call back to that idea of having those end-to-end tests testing your critical paths to at least make sure that those things work together in the happy way. Because I have seen, especially with apps that have a lot of service objects, for some reason, those being kind of the highest-level test sometimes. But oftentimes, they end up not being composed well, being quite coupled with other service objects. So, you end up with a lot of stubbing of those in your test for them. And I think that's kind of where you can see things start to break down. JOËL: Yep. And when the RailsConf videos come out, I recommend seeing Stephanie's talk, some great gems in there for building a more maintainable test suite. Stephanie and I and, you know, most of us here at thoughtbot, we're testing nerds. We think about this a lot. We've also written a lot about this. There are a lot of resources in the show notes for this episode. Check them out. Also, just generally, check out the testing tag on the thoughtbot blog. There is a ton of content there that is worth looking into if you want to dig further into this topic. STEPHANIE: Yeah, and if you are wanting some, like, dedicated, customized testing training, thoughtbot offers an RSpec workshop that's tailored to your team. And if you kind of are interested in the things we're sharing, we can definitely bring that to your company as well. JOËL: On that note, shall we wrap up? STEPHANIE: Let's wrap up. Show notes for this episode can be found at bikeshed.fm. JOËL: This show has been produced and edited by Mandy Moore. STEPHANIE: If you enjoyed listening, one really easy way to support the show is to leave us a quick rating or even a review in iTunes. It really helps other folks find the show. JOËL: If you have any feedback for this or any of our other episodes, you can reach us @_bikeshed, or you can reach me @joelquen on Twitter. STEPHANIE: Or reach both of us at hosts@bikeshed.fm via email. JOËL: Thanks so much for listening to The Bike Shed, and we'll see you next week. ALL: Byeeeeeeeee!!!!!!!! AD: Did you know thoughtbot has a referral program? If you introduce us to someone looking for a design or development partner, we will compensate you if they decide to work with us. More info on our website at: tbot.io/referral. Or you can email us at: referrals@thoughtbot.com with any questions.

    429: Transforming Experience Into Growth

    Play Episode Listen Later Jun 18, 2024 43:38


    Stephanie has a newfound interest in urban foraging for serviceberries in Chicago. Joël discusses how he uses AI tools like ChatGPT to generate creative Dungeons & Dragons character concepts and backstories, which sparks a broader conversation with Stephanie about AI's role in enhancing the creative process. Together, the hosts delve into professional growth and experience, specifically how to leverage everyday work to foster growth as a software developer. They discuss the importance of self-reflection, note-taking, and synthesizing information to enhance learning and professional development. Stephanie shares her strategies for capturing weekly learnings, while Joël talks about his experiences using tools like Obsidian's mind maps to process and synthesize new information. This leads to a broader conversation on the value of active learning and how structured reflection can turn routine work experiences into meaningful professional growth. Obsidian (https://obsidian.md/) Zettelkasten (https://en.wikipedia.org/wiki/Zettelkasten) Mindmaps in Mermaid.js (https://mermaid.js.org/syntax/mindmap.html) Module Docs episode (https://bikeshed.thoughtbot.com/417) Writing Quality Method docs blog post (https://thoughtbot.com/blog/writing-quality-method-docs) Notetaking for Developers episode (https://bikeshed.thoughtbot.com/357) Learning by Helping blog post (https://thoughtbot.com/blog/learning-by-helping) Transcript:  JOËL: Hello and welcome to another episode of The Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I'm Joël Quenneville. STEPHANIE: And I'm Stephanie Minn. And together, we're here to share a bit of what we've learned along the way. JOËL: So, Stephanie, what's new in your world? STEPHANIE: So, as of today, while we record this, it's early June, and I have started foraging a little bit for what's called serviceberries, which is a type of tree/shrub that is native to North America. And I feel like it's just one of those, like, things that more people should know about because it makes these little, tiny, you know, delicious fruit that you can just pick off of the tree and have a little snack. And what's really cool about this tree is that, like I said, it's native, at least to where I'm from, and it's a pretty common, like, landscaping tree. So, it has, like, really pretty white flowers in the spring and really beautiful, like, orange kind of foliage in the fall. So, they're everywhere, like, you can, at least where I'm at in Chicago, I see them a lot just out on the sidewalks. And whenever I'm taking a walk, I can just, yeah, like, grab a little fruit and have a little snack on them. It's such a delight. They are a really cool tree. They're great for birds. Birds love to eat the berries, too. And yeah, a lot of people ask my partner, who's an arborist, like, if they're kind of thinking about doing something new with the landscaping at their house, they're like, "Oh, like, what are some things that I should plant?" And serviceberry is his recommendation. And now I'm sharing it with all of our Bike Shed listeners. If you've ever wondered about [laughs] a cool and environmentally beneficial tree [laughs] to add to your front yard, highly recommend, yeah, looking out for them, looking up what they look like, and maybe you also can enjoy some June foraging. JOËL: That's interesting because it sounds like you're foraging in an urban environment, which is typically not what I associate with the idea of foraging. STEPHANIE: Yeah, that's a great point because I live in a city. I don't know, I take what I can get [laughs]. And I forget that you can actually forage for real out in, you know, nature and where there's not raccoons and garbage [laughs]. But yeah, I think I should have prefaced by kind of sharing that this is a way if you do live in a city, to practice some urban foraging, but I'm sure that these trees are also out in the world, but yeah, have proved useful in an urban environment as well. JOËL: It's really fun that you don't have to, like, go out into the countryside to do this activity. It's a thing you can do in the environment that you live in. STEPHANIE: Yeah, that was one of the really cool things that I got into the past couple of years is seeing, even though I live in a city, there's little pieces of nature around me that I can engage with and picking fruit off of people's [inaudible 03:18] [laughs], like, not people's, but, like, parkway trees. Yeah, the serviceberry is also a pretty popular one here that's planted in the Chicago parks. So, yeah, it's just been like, I don't know, a little added delight to my days [laughs], especially, you know, just when you're least expecting it and you stumble upon it. It's very fun. JOËL: That is really fun. It's great to have a, I guess, a snack available wherever you go. STEPHANIE: Anyway, Joël, what is new in your world? JOËL: I've been intersecting two, I guess, hobbies of mine: D&D and AI. I've been playing a lot of one-shot games with friends, and that means that I need to constantly come up with new characters. And I've been exploring what AI can do to help me develop more interesting or compelling character concepts and backstories. And I've been pretty satisfied with the result. STEPHANIE: Cool. Yeah. I mean, if you're playing a lot and having to generate a lot of new ideas, it can be hard if you're, you know, just feeling a little empty [laughs] in terms of, you know, coming up with a whole character. And that reminds me of a conversation that you and I had in person, like, last month as we were talking about just how you've been, you know, experimenting with AI because you had used it to generate images for your RailsConf talk. And I think I connected it to the idea of, like, randomness [laughs] and how just injecting some of that can help spark some more, I think, creativity, or just help you think of things in a new way, especially if you're just, like, having a hard time coming up with stuff on your own. And even if you don't, like, take exactly what's kind of provided to you in a generative AI, it at least, I don't know, kind of presents you with something that you didn't see before, or yeah, it's just something to react to. JOËL: Yeah, it's a great tool for getting unstuck from that kind of writer's block or that, like, blank page feeling. And oftentimes, it'll give you a thing, and you're like, that's not really exactly what I wanted. But it sparks another idea, which is what I actually want. Or sometimes you can be like, "Hey, here's an idea I have. I'm not sure what direction to take it in. Give me a few options." And then, you see that, and you're like, "Oh, that's actually pretty interesting." One thing that I think is interesting is once I've come up with a little bit of the character concept, or maybe even, like, a backstory element...so, I'm using ChatGPT, and it has that concept of memory. And so, throughout the conversation, it keeps bringing it back. So, if I tell it, "Look, this is an element that's going to be core to the character," and then later on, I'm like, "Okay, help me brainstorm some potential character flaws for this character," it'll actually find things that connect back to my, like, core concept, or maybe an element of the backstory. And it'll give me like, you know, 5 or 10 different ideas, and some of them can be actually really good. So, I've really enjoyed doing that. It's not so much to just generate me a character so much as it is like a conversation back and forth of like, "Okay, help me come up with a vibe for it. Okay, now that I have a vibe or a backstory element or, like, a concept, help me workshop this thing. And what about that?" And if I want to say, "It's going to be this character class, what are maybe some ways I could develop it that are unusual?" and just sort of step by step kind of choose your own adventure. And it kind of walking me through the process has been really fun. STEPHANIE: Nice. Yeah, the way you're talking about it makes a lot of sense to me how asking it to help you, not necessarily do all of it, like, you know, kind of just spit out something that you're like, okay, like, that's what I'm going to use, approaching it as a tool, and yeah, that's really fun. Have you had good experiences then playing with those characters [chuckles]? JOËL: I have. I think it's also really great for sort of padding out some of the content. So, I had a character I played who was a washed-up politician. And at one point, I knew that I was going to have to make a campaign speech. And I asked ChatGPT, "Can you help me, like...here are the themes I want to hit. Give me a, like, classic, very politician-sounding speech that sounds inspiring but also says nothing at the same time." And it did a really good job of that. And you can tell it, "Oh, that's too long. That's too short. I want three sentences. I want five sentences." And that was great. So, I saved that, brought it to the table, and read out my campaign speech, and it was a hit. STEPHANIE: Amazing. That's really fun. I like that because, yeah, I don't think...I am so poor at just improvising things like that, even though, like, I want to really embody the character. So, that's cool that you found a way to help you be able to do that because that just feels like kind of what playing D&D can be about. JOËL: I've never DM'd, but I could imagine a situation where, because the DMs have to improv so much, and you know what the players do, I could imagine having a tool like that available behind the DM screen being really helpful. So, all of a sudden, someone's just like, "Oh, I went to a place," and, like, all of a sudden, you have to, like, sort of generate a village and, like, ten characters on the spot for people that you didn't expect, or an organization or something like that. I could imagine having a tool like that, especially if it's already primed with elements from your world that you've created, being something really helpful. That being said, I've never DM'd myself, so I have no idea what it actually is like to be on the other side of that screen. STEPHANIE: Cool. I mean, if you ever do try that or have a DM experience and you're like, hmm, I wonder kind of how I might be able to help me here, I bet that would be a very cool experience to share on the show. JOËL: I definitely have to report back here. Something that I've been thinking about a lot recently is the difference between sort of professional growth and experience, so the time that you put into doing work. Particularly maybe because, you know, we spend part of our week doing client work, and then we have part of the week that's dedicated to maybe more directly professional growth: our investment day. How do we grow from that, like, four days a week where we're doing client work? Because not all experience is created equal. Just because I put in the hours doesn't mean that I'm going to grow. And maybe I'm going to feel like I'm in a rut. So, how do I take those four days a week that I'm doing code and transform that into some sort of growth or expansion of my knowledge as a developer? Do you have any sort of tactics that you like to use or ways you try to be a little bit more mindful of that? STEPHANIE: Yeah, this is a fun question for me, and kind of reminds me of something we've talked a little bit about before. I can't remember if it was, like, on air or just separately, but, you know, we talk a lot about, like, different learning strategies on the show, I think, because that's just something you and I are very into. And we often, like, lean on, you know, our investment day, so our Fridays that we get to not do client work and kind of dedicate to professional development. But you and I also try to remember that, like, most people don't have that. And most people kind of are needing to maybe find ways to just grow from the day-to-day work that they do, and that is totally possible, I think. And some of the strategies that I have are, I guess, like, it is really...it can be really challenging to, like, you know, be like, okay, I spent 40 hours doing this, and like, what did I learn [chuckles]? Feeling like you have to have something to show for it or something to point to. And one thing that I've been really liking is these automated check-ins we have at the end of the week. And, you know, I suspect that this is not that uncommon for just, like, a workplace to be like, "Hey, like, how did your week go? Like, what are some ways that it was successful? Like, what are your challenges? Like, where do you need support or help?" And I think I've now started using that as both, like, space for giving an update on just, like, business-y things. Like, "Here's the status of this project," or, like, "Here's, you know, a roadblock that we faced that took some extra time," or whatever. Then also being like, oh, this is a great time to make this space for myself, especially because...I don't know about you, but whenever I have, like, performance review time and I have to write, like, a self-review, I'm just like, did I do anything in the last six months [laughs], or how have I grown in the last six months? It feels like such a big question, kind of like you were talking about that blank page syndrome a little bit. But if I have kind of just put in the 10 minutes during my Friday to be like, is there something that was kind of just for me that I can say in my check-in? I can go back and, yeah, just kind of start to see just, like, you know, pick out or just pay attention to how, like, my 40 hours is kind of serving me in growing in the ways that I want to and not just to deliver code [laughs]. JOËL: What you're describing there, that sort of weekly check-in and taking notes, reminds me of the practice of journaling. Is that something that you've ever tried to do in your, like, regular life? STEPHANIE: Oh yeah, very much so. But I'm not nearly as, like, routine about it in my personal life. But I suspect that the routine is helpful in more of a, like, workplace setting, at least for me, because I do have, like, more clear pathways of growth that I'm interested in or just, like, something that, I don't know, not that it's, like, expected of everyone, but if that is part of your goals or, like, part of your company's culture, I feel like I benefit from that structure. And yeah, I mean, I guess maybe that's kind of my way of integrating something that I already do in my personal life to an environment where, like I said, maybe there is, like, that is just part of the work and part of your career progression. JOËL: I'm curious about the frequency. You mentioned that you sort of do this once a week, sort of a check-in at the end of the week. Do you find that once a week is about the right frequency versus maybe something like daily? I know a lot of these sort of more modern note-taking systems, Roam Research, or Obsidian, or whatever, have this concept of, like, a daily note that's supposed to encourage something that's kind of like journaling. Have you ever tried something more on a daily basis, or do you feel like a week is about...or once a week is about the right cadence for you? STEPHANIE: Listen, I have, like, complicated feelings about this because I think the daily note is so aspirational for me [laughs] and just not how I work. And I have finally begrudgingly come to accept this no matter how much, like, I don't know, like, bullet journal inspirational content I consume on the internet [laughs]. I have tried and failed many a time to have more frequency in that way. But, I don't know, I think it almost just, like, sets me up for failure [laughs] because I have these expectations. And that's, like, the other thing. It's like, you can't force learning necessarily. I don't know if this is, like, a strategy, but I think there is some amount of, like, making sure that I'm in the right headspace for it and, you know, like, my environment, too, kind of is conducive to it. Like, I have, like, the time, right? If I'm trying to squeeze in, I don't know, maybe, like, in between meetings, 20 minutes to be like, what did I learn from this experience? Nothing's coming out [laughs]. That was another thing that I was kind of mulling over when he had this topic proposed is this idea of, like, mindset and environment being really important because you know when you are saying, like, not all time is created equal, and I suspect that if, you know, either you or, like, the people around you and the environment you're in is not also facilitating growth, and, like, how much can you really expect for it to be happening? JOËL: I mean, that's really interesting, right? The impact of sort of a broader company culture. And I think that definitely can act as a catalyst for growth, either to kind of propel you forward or to pull you back. I want to dig into a little bit something you were saying about being in the right headspace to capture ideas. And I think that there's sort of almost, like, two distinct phases. There's the, like, capturing data, and information, and experiences, and then, there's synthesizing it, turning information into learning. STEPHANIE: Yes. JOËL: And it sounds like you're making a distinction between those two things, specifically that synthesis step is something that has to happen separately. STEPHANIE: Ooh, I don't even...I don't know if I would necessarily say that I'm only talking about synthesis, but I do like that you kind of separated those categories because I do think that they are really important. And they kind of remind me a lot about the scientific method a little bit where, you know, you have the gathering data and, like, observations, and you have, you know, maybe some...whatever is precipitating learning that you're doing maybe differently or new. And that also takes time, I think, or intention at least, to be like, oh, do I have what I need to, like, get information about how this is going? And then, yeah, that synthesis step that I think I was talking about a little bit more. But I don't think either is just automatic. There is, I think, quite a bit of intention involved. JOËL: I think maybe the way I think about this is colored by reading some material on the Zettelkasten method of note-taking, which splits up the idea of fleeting notes and literature notes, which are sort of just, like, jotting down ideas, or things you've seen, things that you've learned, maybe a thought you had when you read a particular paragraph in a blog post, something like that. And then, the permanent notes, which are more, like, fully formed thoughts that arise out of the more fleeting ones. And so, the idea is that the fleeting ones maybe you're taking those in a notebook if you're doing it pen and paper. You could be doing it in some sort of, like, daily note, or something like that. And then, those are temporary. They were there to just capture information. Later on, you process that, and then you can throw them out if you need to. STEPHANIE: Yeah, that makes a lot of sense. This has actually been a shift for me, where I used to rely a lot more on memory and perhaps, like, didn't have a great system for taking things like fleeting notes and, like, documenting kind of [inaudible 18:28] what I was saying earlier about how do I make sure that the information is recorded, you know, for me to synthesize later? And I have found a lot more success lately in that fleeting note style of operating. And thanks to Obsidian honestly, now it's so easy to be like, oh, I'm just going to open a quick new file. And I need as little friction as possible to, like, put stuff somewhere [laughs]. And, actually, I'm excited to talk a little bit more about this with you because I think you're a little bit different where you somehow find the time [laughs] and care to create your diagrams. I'm like, if I can, for some reason, even get an Obsidian file open, I'll tab to Slack. And I send myself a lot of notes in my just own personal DM space. In fact, it's actually kind of embarrassing because I use the Command+K shortcut to navigate to my own personal DMs, which you can get to by typing me, like, M-E. And sometimes I've accidentally just entered that into a channel chat [laughs], and then I have to delete it really quick later when I realize what I've done. So, yeah, like, I meant to navigate to my personal notes, and I just put in our team chat, "Me [laughs]." And, I don't know, I have no idea how that comes up [laughs], what people think is going on. But if anyone's listening to this podcast from thoughtbot and has seen that of me, that's what happened. JOËL: You may not be the only one who's done that. STEPHANIE: Thank you. Yeah [laughs], that's good to know. JOËL: I want to step back a little bit because we've been talking about, like, introspection, and synthesis, and finding moments to capture information. And I think we've sort of...there's an unspoken assumption here that a way to kind of turbocharge learning from day-to-day experience is some form of synthesis or self-reflection. Would you agree with that statement? STEPHANIE: Okay. This is another thing that I am perhaps, like, still trying to figure out, and we can figure it out together, which is separating, like, self-driven learning and, like, circumstance-driven learning. Because it's so much easier to want to reflect on something and find time to be, like, oh, like, how does this kind of help my goals or, like, what I want to be doing with my work? Versus when you are just asked to do something, and it could still be learning, right? It could still be new, and you need to go do some research or, you know, play around with a new tool. But there's less of that internal motivation or, like, kind of drive to integrate it. Like, do you have this distinction? JOËL: I've definitely noticed that when there is motivation, I get more out of every hour of work that I put in in terms of learning new things. The more interest, the more motivation, the more value I get per unit of effort I put in. STEPHANIE: Yeah. I think, for me, the other difference is, like, generative learning versus just kind of absorbing information that's already out there that someone else's...that is kind of, yeah, just absorbing rather than, like, creating something new from, like, those connections. JOËL: Ooh. STEPHANIE: Does that [chuckles] spark something for you? JOËL: The gears are turning in my head because I'm almost hearing that as, like, a passive versus active learning thing. But just sort of like, I'm going to let things happen to me, and I will come out of that with some experience, and something is going to happen. Versus an active, I am going to, like, try to move in a direction and learn from that and things like that. And I think this maybe connects back to the original question. Maybe this sort of, like, checking in at the end of the week, taking notes is a way to convert something that's a bit more of a passive experience, spending four days a week doing a project for a client, into something that's a little bit of a more active learning, where you say, "Okay, I did four weeks of this particular type of Rails work. What do I get out of it? What have I learned? What is something new that I've seen? What are some opinions I have formed, patterns I like or dislike?" STEPHANIE: Yeah, I like that distinction because, you know, a few weeks ago, we were at RailsConf. We had kind of recapped it in a previous episode. And I think we had talked about like, oh, do we, like, to sit in talks or participate in workshops? And I think that's also another example of, like, passive versus active, right? Because I 100%, like, don't have the same type of learning by just, you know, listening to a talk that I do with maybe then going to look up, like, other things this person has put out in the world, finding them to talk to them about it, like, doing something with the content, right? Otherwise, it's just like, oh yeah, I heard this talk. Maybe one day I'll remember it when the need arises [laughs]. I, like, have a pointer to it in my brain. But until then, it probably just kind of, like, sits there, and nothing's really happened with it. JOËL: I think maybe another thing that's interesting in that passive versus active distinction is that synthesis is inherently an act of creation. You are now creating new ideas of your own rather than just capturing information that is being thrown at you, either by sitting in a talk or by shipping tickets. The act of synthesizing and particularly, I think, making connections between ideas, either because something that, let's say you're in a talk, a speaker said that sparks an idea for yourself, or because you can connect something that speaker said with another idea that you already have or an idea that you've seen elsewhere. So, you're like, oh, the thing this person is saying connects to this thing I read in a book or something another speaker said in an earlier session, or something like that. All of a sudden, now you're creating these new bits of knowledge, new perspectives, maybe even new mental models. We talked about mental models last week. And so, knowledge is not just the facts that you absorb or memorize. A lot of it is building the connections between those facts. And those are things that are not always given to you. You have to create them yourself. STEPHANIE: Yeah, I am nodding my head a lot because that's resonating with, like, an experience that I'm having kind of coaching and mentoring a client developer on my team who is earlier in her career. And one thing that I've been really, like, working on with her is asking like, "Oh, like, what do you think of this?" Or like, "Have you seen this before? What are your reactions to this code, or, like this comment?" or whatever. And I get the sense that, like, not a lot of people have prompted her to, like, come up with answers for those kinds of questions. And I'm really, really hopeful that, like, that kind of will help her achieve some of the goals that she's, like, hoping for in terms of her technical growth, especially where she's felt like she's stagnated a little bit. And I think that calls back really well to what you said at the beginning of, like, you can spend years, right? Just kind of plugging away. But that's not the same as that really active growth. And, again, like, that's fine if that's where you're at or want to be at for a little while. But I suspect if anyone is kind of, like, wondering, like, where did that time go [laughs]...even for me, too, like, once someone started asking me those questions, I was like, oh, there's still so much to figure out or explore. And I think you're actually really good at doing that, asking questions of yourself. And then, another thing that I've picked up from you is you ask questions about, like, what are questions other people would have? And that's a skill that I feel like I still have yet to figure out. I'm [chuckles] curious what you think about that. JOËL: That's interesting because that kind of goes to another level. I often think of the questions other people would have from a more, like, pedagogical sense. So, I write a lot of blog posts. I write a lot of talks that I give. So, oftentimes when I'm creating that kind of material, there's a bit of an inner critic who's trying to, you know, sitting in the audience listening to myself speak, and who's going to maybe roll their eyes at certain points, or just get lost, or maybe raise their hand with a question. And that's who I try to address those things so that then when I go through it the next time, that inner critic is actually feeling engaged and paying attention. STEPHANIE: Do you find that you're able to do that because you've seen that happen enough times where you're like, oh, I can kind of predict maybe what someone might feel confused about? I'm curious, like, how you got from being, like, well, I know what I would be confused about to what would someone else be unsure or, like, want more information about. JOËL: Part of the answer there is that I'm a very harsh critic myself. STEPHANIE: [laughs] Yes. JOËL: So, I'm sitting in somebody else's talk, and there are probably parts where I'm rolling my eyes or being like, wait a minute, how did you get from this idea to this other thing? That doesn't follow. And so, I try to turn that back towards myself and use that as fuel to make my own work better. STEPHANIE: Yeah, that's cool. I like that. Even if it's just framed as, like, a missed opportunity for people to have better or more comprehensive understanding. I know that's something that you're, like, very motivated to help kind of spread more of [laughs]. Understanding and learning is just important to you and to me. So, I think that's really cool that you're able to find ways to do that. JOËL: Well, you definitely want to, I think, to keep a sort of beginner's mindset for a lot of these things, and one of the best ways to do that is to work with beginners. So, I spent a lot of time, back in the day, for example, in the Elm language chat room, just helping people answer basic questions, looking up documentation, explaining sort of basic concepts. And that, I think, helped me get a sense of like, where were newcomers to the language getting stuck? And what were the explanations of those concepts that really connected? Which I could then translate into my work. And I think that that made me a better developer and helped me build this, like, really deep understanding of the underlying concepts in a way that I wouldn't have had just writing code on my own. STEPHANIE: Wow, forum question answering hero. I have never thought to do that or felt compelled to do that. But I remember my friend was telling me, she was like, "Yeah, sometimes I just want to feel good about myself. And I remember that I know things that other people, like, are wanting to find out," and she just will answer some easy questions on Stack Overflow, you know, about, like, basic Rails stuff or something. And she is like, "Yeah, and that's doing my good deed [laughs]." And yeah, I think that it also, you know, has the same benefits that you were just saying earlier about...because you want to be helpful, you figure out how to actually be helpful, right? JOËL: There's maybe a sense as well that helping others, once more, forces you into more of an active mindset for growth in the same way that interrogating yourself does, except now it's a beginner who's interrogating you. And so, it forces you to think a little bit more about those whys or those places where people get stuck. And you've just sort of assumed it's a certain way, but now you have to, like, explain it and really get into some of the concepts. STEPHANIE: So, on the show, we've talked a lot about the fun things you share in the dev channel in our Slack workspace. But I recently discovered that someone (Was it you?) created an Obsidian MD channel for our favorite note-taking software. And in it, you shared a really cool tool that is available in Obsidian called mind maps. JOËL: Yeah, so mind maps are a type of diagram. They're effectively a tree structure, but they don't really look like that when you draw them out. You start with a sort of topic in the center, and then you just keep drawing branches off of that, going every direction. And then, maybe branches off branches and keep going as you add more content. Turns out that Mermaid.js supports mind maps as a graph type, and Obsidian embeds Mermaid diagrams. So, you can use Mermaid's little language to express a mind map. And now, all of a sudden, you have mind mapping as a tool available for you within Obsidian. STEPHANIE: And how have you been using that to kind of process and experience or maybe, like, end up with some artifacts from, like, something that you're just doing in regular day-to-day work? JOËL: So, kind of like you, I think I have the aspiration of doing some kind of, like, daily note journaling thing and turning that into bigger ideas. In practice, I do not do that. Maybe that's the thing that I will eventually incorporate into my practice, but that's not something that I'm currently doing. Instead, a thing that I've done is a little bit more like you, but it's a little bit more thematically chunked. So, for example, recently, I did several weeks of work that involved doing a lot of documentation for module-level documentation. You know, I'd invested a lot of time learning about YARD, which is Ruby's documentation system, and trying to figure out, like, what exactly are docs that are going to be helpful for people? And I wanted that to not just be a thing I did once and then I kind of, like, move on and forget it. I wanted to figure out how can I sort of grow from that experience maximally? And so, the approach I took is to say, let's take some time after I've completed that experience and actually sort of almost interrogate it, ask myself a bunch of questions about that experience, which will then turn into more broad ideas. And so, what I ended up doing is taking a mind-mapping approach. So, I start that center circle is just a circle that says, "My experience writing docs," and then I kind of ring it with a series of questions. So, what are questions that might be interesting to ask someone who just recently had experience writing documentation? And so, I come up with 4,5,6 questions that could be interesting to ask of someone who had experience. And here I'm trying to step away from myself a little bit. And then, maybe I can start answering those questions, or maybe there are sub-questions that branch off of that. And maybe there are answers, or maybe there are answers that are interesting but that then trigger follow-up questions. And so I'm almost having a conversation with myself and using the mind map as a tool to facilitate that. But the first step is putting that experience in the center and then ringing it with questions, and then kind of seeing where those lead. STEPHANIE: Cool. Yeah, I am, like, surprised that you're still following that thread because the module docs experience was quite a little bit a while ago now. We even, you know, had an episode on it that I'll link in the show notes. How do you manage, like, learning new things all the time and knowing what to, like, invest energy and attention into and what to kind of maybe, like, consider just like, oh, like, I don't know, that was just an experience that I had, and I might not get around to doing anything with it? JOËL: I don't know that I have a great system. I think sometimes when I do, especially a more prolonged chunk of time doing a thing, I find it really worthwhile to say, hey, I don't want that to sort of just be a thing that was in my memory, and then it moves out. I'd like to pull out some more maybe practical or long-term ideas from it. Part of that is capture, but some of that is also synthesis. I just spent two weeks or I just spent a month using a particular technology or doing a new kind of task. What do I have to show for it? Are there any, like, bigger ideas that I have here? Does this connect with any other technologies I've done or any other ideas or theories? Did I come up with any opinions? Did I like this technology? Did I not? Are there elements that were inspirational? And then capturing some of that eventually with the idea of...so I do a sort of Zettelkasten-style permanent note collection, the idea to create at least a few of those based off of the experience that I can then connect to other things. And maybe it eventually turns into other content. Maybe it's something I hold onto for a while. In the case of the module docs, it turned into a Bike Shed episode. It also turned into a blog post that was published this past week. And so, it does have a way of coming back. STEPHANIE: Yeah. Yeah. One thing that sparked for me was that, you know, you and I spend a lot of time thinking about, like, the practice of writing software, you know, in the work we do as consultants, too. But I find that, like, you can also apply this to the actual just your work that you are getting paid for [laughs]. This was, I think, a nascent thought in the talk that I had given. But there's something to the idea of, like, you know, if you are working in some code, especially legacy code, for a long time, and you learn so much about it, and then what do you have to show for it [chuckles], you know? I have really struggled with feeling like all of that work and learning was useful if it just, like, remains in my memory and not necessarily shared with the team or, I don't know, just, like, knowing that if I leave, especially since I am a contractor, like, just recognizing that there's value in being like, oh, I spent an hour or, like, half a day sifting through this complex legacy code just to make, like, a small change. But that small change is not the full value of all of the work that I did. And I suspect that, like, just the mind mapping stuff would be really interesting to apply to more. It's not, like, just practical work, but, like, more mundane, I don't know, like, labor [laughs], if you will. JOËL: I can think of, like, sort of two types of knowledge that you can take out of something like that. Some of it is just understanding how this legacy system works, saying, oh, well, they have this user model that's connected to this old persona table, which is kind of unused, but we sometimes rely for in this legacy case. And you've got to have this permission flag turned on and, like, all those things that you had to just discover by reading the code and exploring. And that's going to be useful to you as long as you work in that legacy codebase, as long as you work through that path. But when you move on to another project, that knowledge probably doesn't serve you a whole lot. There are things that you did throughout that journey, though, that you can probably pull out that are going to be useful to you on other projects. And that might be maybe you came up with a new way of navigating the code or a new way of, like, finding how different pieces were connected. Maybe it was a diagramming tool; maybe it was some sort of gem. Maybe it was just a, oh, a heuristic, like, when I see a model, I like to follow the associations first. And I always go for the hasmanys over the belongstos because those generally lead me in the right direction. Like, that's really interesting insight, and that's something that might serve you on a following project. You can also pull out bigger things like, are there refactoring techniques that you experimented with or that you learned on this project that you would use again elsewhere? Are there ways of maybe quarantining scary code on a legacy project that are a thing that you would want to make more consistent part of your practice? Those are all great things to pull out of, just a like, oh yeah, I did some work on a, like, old legacy part of an app. And what do I have to show for it? I think you can actually have a lot to show for it. STEPHANIE: Yeah, that's really cool. That sounds like a sure way of multiplying the learning. And I think I didn't really consider that when I was first talking about it, too. But yeah, there are, like, both of those things kind of available to you to, like, learn from. Yeah, it's like, that time is never just kind of, like, purely wasted. Oh, I don't know, sometimes it really feels like that [laughs] when you are debugging something really silly. But yeah, like, I would be interested in kind of thinking about it from both of those lenses because I think there's value in what you learn about that particular system in that moment of time, even if it might not translate to just future works or future projects. And, like, that's something that I think we would do better at kind of capturing, and also, there's so much stuff, too, kind of to that higher level growth that you were speaking to. JOËL: I think some of the distinctions we're talking about here is something that was explored in an older episode on note-taking with Amanda Beiner, where we sort of explored the difference between exploratory notes, debugging notes, idea notes, and how note-taking is not a single thing. It can serve many purposes, and they can have different lifespans. And those are all just ways to aid your thinking. But being maybe aware of the kind of thinking that you're trying to do, the kind of notes you're trying to take can help you make better use of that time. STEPHANIE: I have one last question for you before we wrap up, which is, do you find, like, the stuff we're talking about to be particularly true about software development, or it just happens to be the thing that you and I both do, and we also love to learn, and so, therefore, we are able to talk about this for, like, 50 minutes [laughs]? Are you able to make any kind of distinction there, or is it just kind of part of pedagogy in general? JOËL: I would say that that sort of active versus passive thing is a thing that's probably true, just about anything that you do. For example, I do a lot of bouldering. Just going spending a lot of time on the wall, climbing a lot; that's going to help me get better. But a classic way that people try to improve is filming themselves or having a friend film themselves, and then you can look at it, and then you evaluate, oh, that's what I did. This is where I was struggling to get the next hold. What if I try to do something different? So, building in an amount of, like, self-reflection into the loop all of a sudden catalyzes that learning and helps you grow at a rate that's much more than if you're just kind of mindlessly putting time into it. So, I would go so far as to say that self-reflection, synthesis—those are all things that are probably going to catalyze growth in most areas of your life if you're being a little bit more self-aware. But I've found that it's been particularly useful for me when it comes to trying to get better at the job that I do every week. STEPHANIE: Yeah, I think, for me, it's like, yeah, getting better at being a developer rather than being, you know, a software developer at X company. Like, not necessarily just getting better at working at that company but getting better at the skill itself. JOËL: And those two things have a way of sort of, like, folding back into themselves, right? If you're a better software developer in general, you will probably be a better developer at that company. Yes, you want domain knowledge and, like, a deep understanding of how the system works is going to make you a better developer at that company. But also, if you're able to find more generic approaches to onboard onto new things, or to debug more effectively, or to better read or understand unknown code of high complexity, those are all going to make you much better at being a developer at that company as well. And they're transferable skills, so they're all really good things to have. STEPHANIE: On that note. Shall we wrap up? JOËL: Let's wrap up. STEPHANIE: Show notes for this episode can be found at bikeshed.fm. JOËL: This show has been produced and edited by Mandy Moore. STEPHANIE: If you enjoyed listening, one really easy way to support the show is to leave us a quick rating or even a review in iTunes. It really helps other folks find the show. JOËL: If you have any feedback for this or any of our other episodes, you can reach us @_bikeshed, or you can reach me @joelquen on Twitter. STEPHANIE: Or reach both of us at hosts@bikeshed.fm via email. JOËL: Thanks so much for listening to The Bike Shed, and we'll see you next week. ALL: Byeeeeeee!!!!!! AD: Did you know thoughtbot has a referral program? If you introduce us to someone looking for a design or development partner, we will compensate you if they decide to work with us. More info on our website at: tbot.io/referral. Or you can email us at: referrals@thoughtbot.com with any questions.

    428: Ruminating on Ruby Enumerators

    Play Episode Listen Later Jun 11, 2024 35:44


    Joël explains his note-taking system, which he uses to capture his beliefs and thoughts about software development. Stephanie recalls feedback from her recent RailsConf talk, where her confidence stemmed from deeply believing in her material despite limited rehearsal. This leads to a conversation about the value of mental models in building a comprehensive understanding of a topic, which can foster confidence and adaptability during presentations and discussions. The episode then shifts focus to the practical application of enumerators in Ruby, exploring various mental models to understand their functionality better. Joël introduces several metaphors, such as enumerators as cursors, lazy collections, and sequence generators, which help demystify their use cases. Episode on note-taking (https://bikeshed.thoughtbot.com/357) What we believe about software (https://bikeshed.thoughtbot.com/172) Ruby Enumerators (https://ruby-doc.org/3.3.1/Enumerator.html) Enumerator Lazy (https://ruby-doc.org/3.3.1/Enumerator/Lazy.html) Modeling a Paginated API as a lazy stream (https://thoughtbot.com/blog/modeling-a-paginated-api-as-a-lazy-stream) Solving a memory performance issue with enumerator (https://thoughtbot.com/blog/how-we-used-a-custom-enumerator-to-fix-a-production-problem) Find in batches (https://api.rubyonrails.org/classes/ActiveRecord/Batches.html#method-i-find_in_batches) Binary tree implementation with different traversals (https://gist.github.com/JoelQ/02f3ef9f61bebc7c8e5ea67d10ed92c6) Teaching Ruby to Count (https://www.youtube.com/watch?v=PHMOsTK1jSE) Transcript:  STEPHANIE: Hello and welcome to another episode of The Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I'm Stephanie Minn. JOËL: And I'm Joël Quenneville, and together, we're here to share a bit of what we've learned along the way. STEPHANIE: So, Joël, what's new in your world? JOËL: So, what's new in my world isn't exactly a new thing. I've talked about it on the podcast here before, and it's my note-taking system. I have a system where I try to capture notes that are things I believe about software or things I think are probably true about software. They're chunked up in really small pieces, such that every note is effectively one small thesis statement and a paragraph of text, and maybe a diagram or a code snippet to support that. And then, it's highly hyperlinked to other notes. So, I sort of build out some thoughts on software that way. A thing that I've done recently that's been pretty exciting with that is introducing a sort of separate set of notes that connect to my sort of opinion notes. So, I create individual notes for public works that I've done, things like blog posts or conference talks. Because a lot of those are built on top of ideas that have been sitting in my note system for a while. Readers and listeners get to sort of see the final product, but often sort of built up over several months or even a couple of years as I added different notes that kind of circled a topic and then eventually got to a thing. What I did, though, was actually making those connections explicit. And so I use Obsidian. Obsidian has this cool graph view where it just sort of shows all of the notes, and it circles them with, like, connections between them where the notes connect. So, I can now see in a visual format how my thoughts cluster in different topics, but then also which clusters have talks and blog posts hanging off of them and also which ones don't, which ones are like, oh, I have a lot of thoughts on this topic, and I've not yet written about it in a public forum; maybe that would be a thing to explore. So, seeing that visual got me really excited. I was having a good time. STEPHANIE: Yes, I have several thoughts coming to mind in response, which is, I know you love a visual. I really like the system of, even if you have created content for it, like, you have a space for, like, thoughts about it to evolve. Because you said, like, sometimes content comes out of notes that you've been...or, like, thoughts you've been having over years, but it's like, even afterwards, I'm sure there will still be new thoughts about it, too. I always have a hard time finding a place for that thing kind of once I, I don't know, it's like some of that stuff is never really considered done, right? So, that is really cool. And I also was just thinking about an old episode of The Bike Shed back when Chris Toomey and Steph Viccari hosted the podcast called "What We Believe About Software," I think, is the title. And I was just thinking about how, like, if only we could just dump all of your notes [laughs] into some, you know, stream [laughs], and that would be really cool. If we ever do, like, an episode like that, that would be really fun. And I'm sure, you know, you already have this, like, huge bank of ideas [laughs]. JOËL: Yes. It is really fun because I build up...the thoughts are often sort of interconnected, and so they might have a topic, but they are very focused. So, I might have, like, three or four things I believe about a particular topic that cluster together. So, we could...and, actually, I have used, in the past, some of those clusters as initial food for thought for a Bikeshed episode. STEPHANIE: Yeah, that's really neat. I like this idea of a kind of just, like, a repository for putting down what you believe about software as kind of, like, guiding principles for yourself as a developer a little bit. I remember a piece of feedback I got about my RailsConf talk that I gave a few weeks ago, and someone said like, "Oh, you sounded really confident in what you were talking about." And that surprised me because I, like, didn't practice rehearsing giving the talk all that much [laughs]. It's because they had asked like, "Oh, like, did you practice a lot?" or something like that. And I think I realized that I, like, really believed in what I was sharing and kind of that, I think, was perhaps what they were picking up on. And even though, like, maybe the rehearsal of the presentation itself was not where I had spent a lot of time on, I had spent a lot of time thinking about what I wanted to share and just building up my confidence around that. So, I thought that was an interesting connection. JOËL: Yeah, you fully developed the idea. You kind of explored all the side trails, maybe a little bit on your own as well. You're on very familiar terrain. And so, that is a way of building confidence separate from just sort of memorizing a talk. STEPHANIE: Yeah, yeah. Exactly. JOËL: In a sense, I almost feel like that's a better sense of confidence because then you can sort of...you can roll with the punches. You know, if a slide is out of order or something, sure, it maybe messes up a little bit of the narrative that you're trying to say. But you're not like, "Oh no, what is this content?" You're like, "Oh yeah, this thing," and you can dive right into it. Somebody asks you a question, and you're not like, "Oh no, that was not in the script," because, again, you've sort of mastered your topic. You know the area as a whole, even sort of the blurry edges beyond the talk, and can react in a way that is pretty confident. STEPHANIE: Yeah. I still definitely fear the open Q&A. I've never done it before, but maybe one day I will be able to because I just, you know, know my topic so well inside and out [laughs] that I can roll with the punches, as you say. JOËL: Open Q&A is just...it's a roll of the dice. Sometimes, you get some really good conversation topics there, and sometimes, it's just a waste of everyone's time. STEPHANIE: I like that take [laughs]. JOËL: Maybe that should go into the things I believe about software. So, other than receiving feedback about your RailsConf talk, what is new in your world? STEPHANIE: Yeah, so I am wrapping up a pretty large project on my client work that we're hoping to release soon. And, in fact, it's actually being released along with a big announcement from the client company to their customers. Essentially, at a conference, they're going to say like, "Hey, like, we now have this new feature." And so, I think there's some hype generated around it. And this past week, we've been doing a lot of internal testing of the feature because there are a lot of employees of my client company who are, like, pretty big users of the product, which is cool because I think we're getting, you know, we have easy access to people who can give us good feedback. But I am having a hard time with being on the receiving end of the feedback and figuring out, like, what is stuff I need to attend to now before, you know, this big release? And what is stuff that is just kind of, like, general feedback like, "Oh, like, I wish it did this," but, you know, it turns out that that's not really what we were building? And how do I just kind of, like, accept that? You know, it's coming from a good place, but I can't really help them there, at least right now. And that's hard for me because I like helping people, right? And so, if someone says something like, "Oh, like, I wish it did this," or like, "Oh, that's kind of weird," I'm like, "Oh, I want to just, like, fix that for you right now [laughs]." And I suspect that a lot of other devs can relate to this, especially if, like, you know, you've been working on something for a little bit, and it feels...I'm just going to say it: it feels a little precious to me. So, what I'm trying to do today, actually, is not look at any of the feedback at all [laughs] and come at it tomorrow with a bit of a calmer vibe and be able to separate out, like, you know, I think all feedback is informative, but not all of it is useful for you at any given moment. Like, if there are bugs, then those will be my immediate priority. If there's maybe some small tweaks that we can make the feature just a little bit more polished, then I also think those are good. But then we are discovering a few things, too, about, like, what this feature is or could be. And I think those are the things that, you know, need to be brought into a conversation with a broader group and think about, like, is this the direction we want to go? So, that's kind of how I'm bucketing that feedback right now. JOËL: How do you feel about receiving direct feedback versus having something filtered through something like a product team? STEPHANIE: Ooh, that's an interesting question. Because right now we're doing, I think, a mix of both that I'm not sure that I really like. On one hand, when it's filtered, it's hard to get to the root of what someone is asking for. And oftentimes, like, it may not even include enough information after the fact to be able to come at it from a dev perspective. But then direct feedback, I think, is just a little bit overwhelming sometimes. And it can be hard to figure out what to pay attention to if you don't have that, like, input from a product team about, like, what the roadmap is looking like or where, you know, strategically their heads are at. So, one thing that kind of has emerged from this is like, oh, I was getting, you know, notifications for the feedback coming in. And what we did was set up a meeting [laughs] so that we can...maybe all of us can, like, scan it together ahead of time and then come at it with a little bit of context about what's come in but then maybe coalesce around the things that we feel are important. JOËL: Well, you'll have to keep us updated on how that plays out, and we can kind of hear what is the balance that ends up working well for you. STEPHANIE: Yeah, I hope so. I think this is actually maybe something that's a bit underexplored from the dev perspective, you know, that in-between stage of you're not totally done because it's not shipped to the world yet, but, you know, you're starting to get a little bit of that input. And what you do with that? Because I think there is some value in being engaged in that process. JOËL: So, we were talking earlier about this note-taking system that I use and sort of a renewed excitement that I have about it. And one thing that I did when I was going through and finding clusters of things that hadn't been written about was I found that I had a cluster of notes on different mental models that I had for understanding Ruby enumerators, not the enumerable module, but the enumerator object. And I decided, you know what? This would probably make for a good blog post. So, I drafted a blog post, and I've been thinking about this a little bit more recently. So, I've been really hyped about digging into enumerators because of that experience. STEPHANIE: Yeah, that's very cool. I have to say that I feel like I did not know a lot about enumerators and the API for them kind of before you brought this topic up, and I did a bit of a deep dive in preparation for us to discuss it. I feel like most devs, you know, work with enumerators via methods on enumerable without totally knowing that they are. So, I think that this would be a really interesting episode for people to be like, oh, like, I've been using this stuff, you know, the whole time, and now I can have a different perspective or just more insight on what they can do. JOËL: Before we dig into individual mental models, though, I want to think a little bit about the concept of mental models as a whole. Years ago, someone gave me advice to sort of pay attention to mental models, ways I think about the world or different code structures, different code approaches, and that really stuck with me. So, I've since been, like, kind of, like, collecting mental models. And, in a way, they're like a, for me, a bit more of a concrete way to look at a particular topic. So, I can say I'm looking at this particular topic through the lens of a particular mental model that helps me build more clarity around it. And if I have three or four, then I can kind of look at it from three or four different perspectives. And now, all of a sudden, I feel like I'm seeing in three dimensions. STEPHANIE: Whoa, the Matrix even [laughs]. That's cool. Yeah, I really like that advice. I think I'm going to steal it and start kind of suggesting it to other people because I think, in a way, on this show, that has come through a lot. And talking about things on the podcast has helped me develop a lot of my mental models. And I think we've done a few, like, episodes in the past about various ones we have for just our work because it's like, that's infinite [laughs]. But what I really have been appreciating is that mental models just need to work for you. As long as you're able to understand something, then it's valuable. And that has really helped me also, like, just get on the same understanding with others because the goal is not necessarily to, like, explain it the way that I would think of it, but figure out what would help them kind of develop their own mental model for understanding something, and, you know, kind of as long as we both feel like we have that shared understanding, no matter what lens it's through. And, you know, sometimes it's even more effective when we are able to share it. But I feel like, you know, you can still find ways to collaborate on something with a diversity of mental models. JOËL: Yeah, they're a great way to build self-understanding. They're a great way to sort of build understanding between two people. So, I'm a huge fan of the concept. And part of what I've been doing with my note-taking system is trying to capture those as much as possible. If I'm ever, like, trying to understand a complex topic and I'm like, oh, I think I've got a breakthrough here; I understand it; it's kind of like this, or you can imagine it in this perspective, it's like, write that down. That's gold. STEPHANIE: Very cool. So, Joël, would you be able to share some of your mental models for enumerator? JOËL: So, one way that I look at it is the idea that an enumerator is effectively a cursor over a collection. So, you have an array and a regular array; you're either in the middle of iterating through it using something like each, or you're not. You just have a collection of items. Enumerator introduces the idea that you're actually sort of at a position in the array. So, you're sort of focused on, let's say, the third item or the fourth item. You have a cursor there, and you can move that cursor forward as you sort of step through. But the really cool thing is you can also kind of pause and just pass that cursor on to someone else, and someone else can move the cursor a few steps further down the collection, pause, pass it on to someone else. And it's totally fine. Nobody has to, like, go through an entire, like, each iteration. STEPHANIE: Yeah. So, when you were talking about cursors, that got me thinking a little bit because I actually have struggled with that concept, especially when it comes to, you know, things code-related. Like, when I've had to work with database things and stuff, like, the idea of a cursor was a little, like, difficult for me to wrap my head around. And I was looking at the methods on enumerator, like the instance methods on enumerator. And one of them actually is what helped me develop this mental model. And I'm excited to see what you think. But there is a rewind method that basically rewinds the sequence back to its beginning, right? And what that triggered for me was a VHS tape [laughs] and just those, like, car-shaped rewinders for tapes back in the '90s. I don't know if you ever had one in your house, but I did. And I just thought that was such a cool method name because it was very, I don't know, it was just like a word that we use in the English language, right? So, the idea of, like, tapes, you know, like, cassette tapes or VHS tape kind of also it sounds like it matches well with what you were sharing, too, where it's like, I could pass, I don't know, maybe I, like, listen to a few songs on my cassette tape, and then I give it to someone else, and they can pick up where I left off. And yeah, that was really helpful in understanding, like, a marker of a position a little more than cursor was able to for me. JOËL: That's really interesting because now I wonder, like, how far we could push that metaphor. So, musical data is encoded on magnetic tape. Cassette tapes typically there are sort of two spools. You start off with all of the tape wound up around one spool, and then as it sort of moves across the read head, it gets wound up on sort of the, I don't know, destination spool. I guess you can call them origin and destination. And because of that, you can sort of be in a, like, partly read state where, you know, half the tape is on the destination spool, half of it is on the origin spool, and you have that read head that's in the middle, and you're just kind of paused there. And you can kind of jump forward in that. So, I imagine something like that in your metaphor is like an enumerator. Contrast that to imagine just a single spool, which is just we have musical data encoded on magnetic tape, and we wrapped it up on a spool. I feel like that's almost more like a regular array because you don't have that concept of, like, position, or being able to read parts of it or anything like that. It's just, here's some data. STEPHANIE: Yeah. While you were talking about the two spools, I was thinking about, like, part of what is nice about enumerator is that you can go forward or backwards, right? And that feels a little more possible with that two-spool metaphor [laughs], rather than just unraveling something, where you are kind of discarding what has already been read. JOËL: The one caveat there is that enumerators can move forward one item at a time. They can only move backwards by jumping back to the beginning. So, you can't step forward or step back. STEPHANIE: Yeah, that's fair. JOËL: You step forward, or you, like, rewind to the beginning. I think, in my mind, I was thinking a little bit more about this metaphor. And I think it's also just a metaphor for what's called the External Iterator Pattern. It's one of the classic Gang of Four Patterns, which is what enumerator, the object in Ruby, is an implementation of. I feel like I always see that in the documentation, like, oh, enumerator is an implementation of the External Iterator Pattern. And I just kind of go, what? STEPHANIE: [laughs] JOËL: Or maybe I kind of understand the idea of, like, okay, it's a way to, like, be able to step through a collection. But thinking in terms of a cursor or even your model as a cassette tape, I think that gives me a model, not just for enumerators, but then for better understanding that external iterator pattern. Like, I'm now not going to think of if I'm ever reading through the Gang Of Four book, or some other languages say we're an doing External Iterator Pattern, and I'll immediately be like, oh, that's a cursor, or that's a cassette tape. STEPHANIE: Yeah, very cool. I like it. JOËL: Another mental model that I have is thinking of enumerator in terms of a lazy collection. This is something that you tend to see more in functional programming languages, so the idea that you have a collection of potentially infinite length, or it could even be unknown length. But each element only sort of comes into being as you attempt to read it. So, it's kind of, like, a potentially infinite chain of Schrodinger's boxes. And you've got to open each of them to find out what's inside. STEPHANIE: Do you know what this reminded me of? Like elementary school math questions that were like, "What comes next in this pattern?" And it has, like, you know, the first, like, four or five values in a sequence or something. And then, you have to figure out, like, what the next value is. But then, in some ways, you know, I think it can depend on whether your enumerator is using the previous value to determine the next one. But yeah, it's like, you can't just jump ahead to figure out what the 10th, you know, value in this pattern is without kind of knowing what's come before it. JOËL: And sort of that needing to step through the entire collection, sort of one element at a time. STEPHANIE: Yeah, exactly. JOËL: I think a way that that concept is interesting, to me, is situations where a collection might be expensive, and you don't necessarily need all of it. So, you might have a bunch of calculations, but you can stop when you've hit the first one that succeeds or that matches a certain criteria. And so, it's not worth it to calculate the entire array of calculations if you're going to stop at the third one. And you could do that with some sort of, like, loop or something like that. But having it as a collection means you get to just treat it like an array, and you can call detect on it and do all the nice things that you're used to. It just happens to be a little bit more efficient in terms of not creating more data than you need to. STEPHANIE: Yeah. And I think there's some really cool stuff you can do when you start chaining enumerators with this concept of it being lazy evaluated. So, one of the things I learned in my deep dive is that when you are using the lazy method, you're able to chain enumerators. And they work a bit differently, where the default functionality is, like, everything in the collection gets evaluated through the first method, and then it gets iterated over in the second method. Whereas if you use lazy, I believe how it works is that, like, the first value gets kind of processed by all of the methods. And then, you get, you know, the output before moving on to the second, like, the next value. Does that sound right? JOËL: Yes. And I think that's where there's often a lot of confusion because there's sort of plain enumerator, and then there's a lazy enumerator that Ruby provides. A plain enumerator is a lazy list in the sense that items don't get evaluated unless you try to reach for them. So, if you have an enumerator and you say, "Just give me the first five items," it will do that. And even if the collection was 200 items long, the next 195 don't get evaluated. So, that's very efficient there. Where you would get into trouble is that plain enumerators are not lazy when it comes to traversals. So, any method that would traverse the entire collection, so something like a map or a select, is not going to be lazy because it's going to traverse the entire collection, therefore forcing us to evaluate each of the items in there. Whereas something like enumerable lazy will not actually traverse the collection when you do your map or you're selecting. It will wait for you to say, "Give me the first item," or "Give me the first ten items," or something like that. But you don't always need lazy. You really only need lazy when you're doing a traversal method. STEPHANIE: Okay. Cool, cool, cool. That makes a lot of sense. JOËL: I think a sort of spinoff metaphor that I have there is this idea of a lazy list. Another concept that, in my mind, is very adjacent to lazy lists is the concept of streams. And streams I typically think of them in terms of, like, files or networking, things like that. But a thing that you can do let's say you're working on data that's in a very large file, so big that you can't fit it into memory, a common solution there is streaming it. So, you don't load the entire file into memory and then operate on it. Instead, little chunks of it are loaded into memory. You operate on them, and then you release that memory and load the next chunk. So, you sort of work through that file in chunks, but you'd only have, you know, 1 line or ten lines or however big your chunk is in memory at a time. An enumerator allows you to do that with things that are not files. So, this could be a situation where, let's say, you're reading a lot of data from the database. You just have too many rows. You can't load them all into memory at once. But you do want to traverse through them. You could chunk that using enumerator so that every, you know, it loads 100 rows at a time or 1,000 rows at a time, or something like that. And your enumerator allows you to treat that as though it's a single array, even though, in the background, it's being chunked into pieces so that you never have more than a thousand rows at a time in memory. So, it allows you to do some, like, really nice sort of memory performance things. STEPHANIE: When would you want to use this over kind of something like batching queries? JOËL: So, I think ActiveRecord findinbatches does something like this under the hood. STEPHANIE: Oh, cool. JOËL: I don't know if they use Ruby's enumerator or if they sort of build their own custom extension to it, but it's built on this idea. STEPHANIE: Okay, that's really neat. I have another mental model that I wanted to get your thoughts on. JOËL: Yeah! STEPHANIE: One of the ways that I looked up that you can construct an enumerator, an infinite enumerator like we were talking about a little bit earlier, was with the produce class method. And that actually got me thinking about a production line and this idea that, you know, you have this mechanism for, you know, producing some kind of material or, like, good or something like that. And it's just there and waiting and ready [laughs] for you to, like, kind of ask for it, like, what it needs to do. And you can do that, like, sometimes in batches, right? If you are asking for like, "Okay, I want a thousand units," and then the production line goes to work [laughs]. But yeah, that was another one of those things where I'm like, wow, they really, I think, came up with a cool method name that evoked, like, an image in my head. JOËL: That's the power of naming, right? And I think it's interesting you've mentioned twice how going through the method names on enumerator and finding different method names all of a sudden, like, turned on a light bulb in your mind. So, if you're naming things well, it can be incredibly useful for users of your library to pick up on what you're trying to do. So, I want to circle back to something that you mentioned earlier, the idea of elementary school quizzes where you have to, like, figure out the next item in the sequence. Because that, for me, is very similar to my mental model: the idea that an enumerator is a sequence generator. So, instead of thinking of it as, oh, it's like an array or it's some kind of collection, instead, think of it as a robot that I can just ask it, hey, give me a value, and it will give me a value. And then, it will, like, keep doing that as long as I keep asking it for it. And those values, you know, they could be totally random. You can build one of those. But you can also have it so that the values sort of come from a sequence. It's not like an array where you're like, oh, I'm going to, like, predefine an array of, I don't know, the Fibonacci sequence, and when someone asks me for the third value, I'll just go and read that third value from the array. Instead, it knows the algorithm, and it just says, "Oh, you want the next value in the Fibonacci sequence? Let me calculate it. Here it is. Oh, you want the next value? Here it is." And so, thinking from that perspective helped me really come to terms with the concept that values really do get calculated just in time. It's not really a collection. It's an object that can give you new values if you ask it. STEPHANIE: Yeah, okay. That is making a lot more sense kind of in conjunction with the lazy list model that you shared earlier, and even a little bit with the production line that I was kind of sharing where it's like, you know, in this case, kind of, it's, like, the potential for a value, right? JOËL: Right, exactly. And, you know, these are all mental models that converge on the same ideas because they're all just slightly different perspectives on what the same object does. And so, there is going to be some overlap, some converging between all of them. I have another fun one. Can I throw it at you? STEPHANIE: Please. JOËL: This one's a little bit different, and it's the idea that enumerators are a tool to bring your own iteration to a collection. So, imagine a situation where you're building your own, let's say, binary tree implementation. And there are multiple ways to traverse through a binary tree. In particular, let's say you're doing depth-first search. There are sort of three classic ways to traverse that are called pre-order, post-order, and in-order traversals. And it really is just sort of what order do you visit all the children in your tree? Now, the point of a collection, oftentimes, is you need a way to iterate through it. And a classic solution would be to include enumerable, the module. In order to do that, you have to define a way to iterate through your collection. You call that each. And then, enumerable just gives you all the other nice things for free. The question is, though, for something like a tree where there are multiple valid ways to traverse, which one do you pick to make it the each that gets sort of all the enumerable goodies, and then the others are just, like, random methods you've defined? Because if you define, let's say, pre-order traversal as each, now your detect and select and all those are going to work in pre-order, but the others are not going to get that. So, if you map over a tree, you're forced to map over in pre-order because that's what the library author chose. But what if you want to map over a tree in post-order or in-order? STEPHANIE: Yeah, well, I'm guessing that here's where enumerator comes in handy [laughs]. JOËL: Yes. The approach here is instead of designating sort of one of those traversals as the sort of blessed traversal that gets to have enumerable; you build three of these, one for each of these traversals. And then, what's really nice is that because enumerators are themselves enumerable, they have map and select and all of these things built in. Now you can do something like mytree dot preorder dot map or mytree dot postorder dot map. And you get all the goodies for free, but the users of your library get to basically choose which traversal they want to have. As a library author, you're not forced to pick ahead of time and sort of choose; this is the one I'm going to have. You sort of bring your own traversal by providing an enumerator, and then everything else just kind of falls into place. STEPHANIE: Bring Your Own Traversal (BYOT) [laughter]. I like it. Yeah, that's cool. I can see how that would be really handy. I have not yet encountered a situation where I needed to get that deep into how my iteration is traversed, but that's really interesting. And, I mean, I can start even imagining, like, having an each method defined in these different ways, and then all of that being able to be composed with some of the other...just other methods. And now you have, like, so many different ways to perhaps, like, help, you know, different performance use cases. JOËL: Yeah, it can be performance. I often tend to think of enumerator as a performance thing because of its sort of lazy properties because; it allows you to sort of stream or chunk data that you're working with. But in the case of this mental model of the Bring Your Own Traversal, it actually is more about flexibility and having sort of the beauty of Ruby without having to compromise on, oh, I have to pick a single way to traverse a collection. STEPHANIE: But I really appreciate kind of this discussion about enumerator because this was previously, like, I don't think I have really ever used the class itself to solve a problem, but now I feel a lot more equipped to do so with a couple of the different kind of perspectives. And I think what they helped me do is just prime myself. If I see a problem that might benefit from something being iterated in a lazy way, like, being like, oh, I remember this thing, this mental model. Now I can go kind of look at the documentation for how to use it. And yeah, like, I don't know how I would have stumbled across, like, reaching for it otherwise. JOËL: That's a really interesting thing to notice because we've been talking a lot about how mental models can be a tool for understanding. But once you build an understanding, even though it's somewhat fuzzy, they're also a great tool for sort of recall. So, not only are you thinking, okay, well, this mental model says enumerators are kind of like this, or they function in this way. On the flip side of it, you can say, "Well, lazy evaluation problems are often enumerator problems. Like, streaming or chunked data problems are often enumerator problems. Multiple traversals are enumerator problems." So, now, even though you don't, like, fully understand it in your mind, you've got that recall where you can enter it, where you can come across that problem, and immediately you're like, oh, I'm dealing with multiple traversals here. I don't remember exactly how, but somehow, in my mind, I've got a connection that says, "Enumerators are a solution for this. Let me dig into that." STEPHANIE: Yeah, especially as an alternative to where I would normally reach for something...a more kind of common enumerable method. Because I definitely know that feeling of like, oh, like, I wish it could just, like, do this a little bit differently, you know. And it turns out that, you know, something like that probably exists already. I just needed to know what it was [laughs]. JOËL: On that theme of I wish that I could have something that behaved just a little bit more...like, I'm doing something slightly weird, and I wish they would behave more, like, just plain Ruby does normally with my, like, collections I'm familiar with. I'm going to pitch a talk that I gave at RubyConf Mini called "Teaching Ruby to Count." Some of these mental models actually showed up there. But the whole idea is like, oh, if you're bringing in sort of more custom objects and all of that, how can you just tweak them a little bit so that they're just as joyful to use and interact with as arrays, and numbers, and ranges? And they just sort of fit into that beauty of Ruby that we get out of the box. STEPHANIE: Awesome. On that note, shall we wrap up? JOËL: Let's wrap up. STEPHANIE: Show notes for this episode can be found at bikeshed.fm. JOËL: This show has been produced and edited by Mandy Moore. STEPHANIE: If you enjoyed listening, one really easy way to support the show is to leave us a quick rating or even a review in iTunes. It really helps other folks find the show. JOËL: If you have any feedback for this or any of our other episodes, you can reach us @_bikeshed, or you can reach me @joelquen on Twitter. STEPHANIE: Or reach both of us at hosts@bikeshed.fm via email. JOËL: Thanks so much for listening to The Bike Shed, and we'll see you next week. ALL: Byeeeeeeee!!!!!!! AD: Did you know thoughtbot has a referral program? If you introduce us to someone looking for a design or development partner, we will compensate you if they decide to work with us. More info on our website at: tbot.io/referral. Or you can email us at: referrals@thoughtbot.com with any questions.

    427: RailsConf Recap and Conversing About Coupling

    Play Episode Listen Later May 28, 2024 37:03


    Joël and Stephanie talk RailsConf! (https://railsconf.org/). Joël shares how he performed as a D&D character, Glittersense the gnome, to make his Turbo features talk entertaining and interactive. Stephanie's talk focused on addressing test pain by connecting it to code coupling, offering practical insights and solutions. They agree on the importance of continuous improvement as speakers and developers and trying new approaches in talks and code design, and recommend Jared Norman's RailsConf talk on design patterns, too! That One Thing: Reduce Coupling for More Scalable and Sustainable Software (https://www.informit.com/articles/article.aspx?p=2222816) Connascence.io (https://connascence.io/) [Connascence as a vocabulary to discuss coupling](https://thoughtbot.com/blog/connascence-as-a-vocabulary-to-discuss-coupling](https://thoughtbot.com/blog/connascence-as-a-vocabulary-to-discuss-coupling) The value of specialized vocabulary (https://bikeshed.thoughtbot.com/356?t=0) Transcript: We're excited to announce a new workshop series for helping you get that startup idea you have out of your head and into the world. It's called Vision to Value. Over a series of 90-minute working sessions, you'll work with a thoughtbot product strategist and a handful of other founders to start testing your idea in the market and make a plan for building an MVP. Join for all seven of the weekly sessions or pick and choose the ones that address your biggest challenge right now. Learn more and sign up at tbot.io/visionvalue.  JOËL: Hello and welcome to another episode of The Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I'm Joël Quenneville. STEPHANIE: And I'm Stephanie Minn. And together, we're here to share a bit of what we've learned along the way. JOËL: So, Stephanie, what's new in your world? STEPHANIE: So, I think I can speak for both of us and say what's new in our world is that you and I just came back from RailsConf in Detroit. JOËL: Yeah, we were there for, I guess, it's a three-day conference. Both of us were giving talks. STEPHANIE: Yeah. I don't think we've both spoken at a conference for at least a little over a year, so that was really fun kind of to catch up in person. And there was a whole crew of thoughtboters who were there. Yeah, I feel like we were hanging out, like, a lot [chuckles] all of last week, just seeing each other, talking about, you know, rehearsing our talks and spending time together on...there was, like, a hack day, and we were sitting at the table together. So, I feel like I'm totally caught up on everything that's new in your world, and that's it. That's the end of the show [laughs]. JOËL: On that note, shall we wrap up? STEPHANIE: [laughs] That would not be very fair to our listeners. [laughter] JOËL: Yeah. So, how was the conference speaking experience for you? STEPHANIE: Ooh, it was really great this year. I have not spoken at a RailsConf before, so this was actually, I think, a bigger stage than I had experienced before, and I had a great time. I met Ruby friends, new and old, and, yeah, I left feeling very gooeyed, and very energized, and just so grateful for the Rails community [laughs]. Yeah, I had a very lovely time, kind of being a little bit outside my normal life for a few days. And I think my favorite part about these things is just like, anywhere you go, you can kind of just have a shared interest with someone, and you can start a conversation with them. JOËL: That's really interesting. Do you find yourself just reaching out to strangers at conferences like this? Or do you tend to just hang out with the people that you know? STEPHANIE: Oh, I think a little bit of both. I like to get meals with people I know. But if I'm just hanging out in, like, the lobby or if I happen to get a seat for a talk and I'm sitting next to someone that I don't know, I find it quite easy to just be like, "Hi, like, I'm Stephanie. Are you excited for this talk?" Or, like, "What good talks have you seen recently?" There's an aspect of, like, the social butterfly that comes out of me when I'm at these things. Because I just don't get to have, like, easy access to, I don't know, people with, like, that shared interest or people who are willing to just have a conversation with you normally, I think. JOËL: Yeah, would you describe yourself more as an introvert or an extrovert? STEPHANIE: I am an extroverted introvert [laughter]. I feel like maybe that might be interpreted as a non-answer, but I think I lean more on the introvert side. But you know when you're with a group of people, and there's not, like, a very clear extrovert in that conversation, and then you're like, oh, I have to do the heavy [chuckles] lifting of the social lubrication [laughs] in this conversation, I can step into that role, reluctantly [laughs]. JOËL: Okay. I like the label that you used, the extrovert introvert, in that I enjoy social situations. I do well in social situations. But they also consume a lot of energy for me. I don't necessarily get sort of recharged by doing social events. So, people will be surprised when they find out that I tend to talk about myself as an introvert because, like, "Oh, but you're, like, you know, you're not awkward. You engage very well in different group situations." STEPHANIE: You have a podcast [laughs]. JOËL: And the truth is I enjoy those things, right? I really like social interaction, but it does, after a while, wear me out. STEPHANIE: Yeah, that makes sense. I did want to spend a little bit of time talking about the talk you gave at RailsConf this year: "Dungeons & Dragons & Rails." JOËL: I got to have a lot of fun with the theme. The actual content was introducing people to Turbo by building an interactive Dungeons & Dragons character sheet using vanilla Rails and a little bit of Turbo. So, we're not even writing any JavaScript. We're just using the Turbo helpers, a little bit of Action Cable to mimic something a little bit like...people who are in the know might be familiar with the site D&D Beyond, which is kind of the official D&D online character sheet website. Of course, it wasn't anywhere near as fancy because it's a 30-minute talk and showcasing different features, but that's what we were aiming for. STEPHANIE: Yeah, you know, you've talked a bit about giving talks on the show before, but I wanted to get into what made this one different because I think it could be fun for our listeners. [laughter] JOËL: The way I structured this talk so it has a theme. It's about Dungeons & Dragons, and we're building a character sheet. The way I wrote the talk was it's broken up into chapters. Each chapter is teaching a new feature in Turbo that I want to show off. In order to motivate learning each of these features...because I don't like to just say, "Oh, here's a thing that technology can do. Oh, here's a thing that technology can do." That's boring. You need a reason to learn that. So, I needed a reason to say, "We need to add this to a character sheet." So, every sort of chapter of the talk opens up with a little narrative portion. We're following this character, Glittersense, the gnome, and he's on adventures. And at different points in the adventures, he's going to do different types of roles or need different stats and things. And so, when we reach the point in the adventure where we need that, we sort of freeze frame and then say, "Okay, let's add that as a feature to the character sheet." And then, oh no, it turns out that this feature is a little bit more complicated. We're going to have to learn a new Turbo feature to do that. Who would have guessed? And then, we learn a new Turbo feature together. And then, we go back to the narrative portion. The adventures of Glittersense continue. And then, oh no, we're going to need to add another feature to the character sheet. And that's sort of how the talk is structured. STEPHANIE: Yeah. And you did a really cool thing with the narrative portions, which was you basically performed as Glittersense, the gnome, voice and posture, and a lot of really great acting from you [laughs], in my opinion. JOËL: That is something that came out pretty late in the talk preparation. So, I knew I wanted this kind of alternating story and code structure. Then, like, the weekend before RailsConf, I'm running through my slide deck, and I realized, you know what? What if instead of narrating Glittersense's adventures, what if I went first person for those sections? Glittersense tells his own story. And then, from there, it wasn't a big jump to say, you know what? This is D&D. If I'm going first person and narrating, I really should do a voice. And this is a conversation I had with a couple of people at the speaker dinner. And, of course, everyone's like, "You should 100% do the voice." And I was really not feeling confident in my ability to pull it off. So, for the next two nights, because I was speaking on the third day, the next two nights at the conference, in the evenings, I'm in the hotel room in front of the mirror just practicing my gnome voice to try to get something that got the persona of Glitterense, the gnome, across to the audience. STEPHANIE: How would you describe the persona? JOËL: Very extra. STEPHANIE: [laughs] JOËL: Very high energy. STEPHANIE: Yes. The name Glittersense is very extra, after all. JOËL: [laughs]. I punctuated a lot of the things that he says with just high-pitched laughter. He's also...so, the framing device for all of this is that you're in a tavern listening to him tell his adventures. I wanted a little bit of the sense that Glittersense is maybe embellishing a little bit. I think it may be too much to say he's full of himself, but he's definitely making himself to be the hero of the story, and maybe making himself to be slightly cooler than he really was. STEPHANIE: Yeah. I definitely got, like, a little bit of eccentricity, too, from the persona. And you know when you just, I don't know, meet an older person who has, like, a lot of life experience, and they want to tell you about it [laughter], but you do kind of maybe have a little bit of suspicion around how much they're exaggerating [laughs]. But it was really fun. Everyone I talked to afterwards, like, loved it. And I got to share the little nugget that, like, oh yeah, and Joël only, like, started doing the voice, like, decided that he was going to do it two days ago. And they were just all really, like, blown away because it seemed so well practiced, and it was really fun. JOËL: I got to do something really fun, also, with physical space because Glittersense narrates his portion, sort of the story portions, but then the code portions where we're talking about Turbo, I'm talking in my own voice. And so, when I'm talking about Turbo, I'm standing at the lectern. And when I'm Glittersense, I'm kind of off to the side on the stage and doing the voice. And so, there's this almost, like, two worlds that are inhabited: one by Joël, the speaker, and one by Glittersense, the gnome. And it got to the point where I don't say or do anything. I only move from the lectern to the, like, portion of the stage where Glittersense lives. And the audience starts chuckling and, like, nothing has happened yet, like, no jokes have been told. No voice has happened. No slides have changed. But the anticipation, people know what's coming. STEPHANIE: Yeah. And I think the best part, what I really found just really fun and, I don't know, every time it happened, I just really enjoyed it, when you transitioned out of Glittersense, the gnome, and back to Joël because you were so nonchalant about it. You kind of, like, straighten up rather than having your little kind of crouchy gnome posture, and then just walk across back to the podium. And then, in your normal voice, go back to just, you know, sharing very...not necessarily dry, but just, like, straight to the point. "And this is, like, how you, you know, create a frame in [laughs] Turbo," as if nothing happened [laughs] when even just, like, you know, 20 seconds ago, you were just enthusing about, like, slaying the bandit, chieftain [laughter] known as Glittersense. JOËL: Uh-huh. I think, especially when I open, so I get introduced. I'm off stage. I walk onto the stage, and I'm immediately Glittersense. And I'm telling a story, and the intro goes on for, like, quite a while. It's a big story chunk. And then, at some point, I just walk over to the lectern, drop the voice, hit next slide, and it's my title slide. I'm just like, "Okay, now welcome to Dungeons & Dragons on Rails. We're going to build a character sheet together." STEPHANIE: Yeah, that's exactly the moment I'm thinking of. JOËL: The walking in as Glittersense and just immediately going to the voice caught everyone by surprise. And then, the, like, oh, he keeps going for this. Is the whole talk going to be like this? And then, the, like, just when you think, oh, he's really going for it, the, like, dropping it and going to the podium and title slide. It wasn't intended to be a funny moment, but I think the contrast and the fact that I just switched over was one of the biggest laughs I got. STEPHANIE: Yeah, I mean, I think that attests to how good the delivery of it was because that contrast was very felt. So, props to you. JOËL: I love the idea of, you know, the thought that you put into building a talk and, like, the narrative structure and the pedagogy of the stuff. And, I think, in this particular case, this is almost like a narrative approach called in media res, where you start kind of in the middle. You open your book, or your movie, or whatever in the middle of the story. And then, you kind of come back to the beginning at some point later. So, it starts with some kind of action scene that grabs your attention. So, in this case, my title slide is 10, 15 slides into the talk. We get immediately started with Glittersense and his adventures. And then, once we're sort of all bought into this world, then we move to the title slide and talk about, okay, we're here to build a character sheet and all that stuff. And I think that it wouldn't have had the same impact if I'd, like, opened with that and then gone into Glittersense's adventures. And that's something that was not the case at the beginning. I really reworked the talk to make it in that order. And I think that the talk had a lot more impact for doing that. STEPHANIE: Yeah, definitely. I guess I also just wanted to point out that this is very different from all your other talks. And I think it's really cool that, you know, you are a veteran speaker, but you still find ways to do something new and try something that you've never done before, and yeah, find ways, new ways to, like, speak and engage people and teach. I don't know, do you have just any thoughts about why or how you got into a position to be like, "Oh, you know, I'm going to do something super different this time around" [laughs]? JOËL: So, every talk I give, I try to do something new, something different, to push myself as a speaker to get better. That might be in the writing of the talk; that might be in the delivery. More recently, I've been trying to do more with dynamic presence on stage. So, when I spoke at RubyConf San Diego, I was trying to not just stand at the lectern but to learn to be able to give my talk while also, you know, walking around the stage, looking at the audience, making pauses where it's necessary, not to just be so into the delivery of the talk by just standing at the podium and, like, going through my deck, which is a small thing but I think is an area I wanted to improve in. This time, I was playing around with some more narrative framing and ended up, yeah, like, pushing it to an extreme. And it works with the theme because inhabiting a character and role-playing is the core part of D&D. Not everybody plays a D&D character by doing a voice. You are a little bit extra if you do that. But it's not uncommon for people to do a voice. And so, it kind of fit perfectly with my theme. I just needed to get the self-confidence to do it. So, thank you to everyone at the speaker dinner that was like, "No, you totally got this. You should do this," because I was feeling very unsure. STEPHANIE: It really paid off, so... JOËL: I'd like to circle back to your talk, though. So, you gave, basically, the first talk of the conference. You were the first session after the keynote. A theme that came up multiple times in your talk was this idea of coupling and how it affects different parts of our code and, particularly the way that we structure tests or the way that we feel test pain. How did you, when you were prepping this talk, discover that theme and decide to lift it up? Was that something that you knew ahead of time you wanted to talk about, or did it just sort of emerge as part of the talk preparation process? STEPHANIE: That's a really great question, and I'm glad you picked up on that. So, my talk was called: "So, Writing Tests Feels Painful. What Now?" Originally, when I came up with this idea, it actually started with coupling. I realized that I wanted to give a talk about coupling because it's just something that I was struggling with or, like, had seen other people struggle with and really wanting kind of a discrete resource, wanting to provide that. But as I was just thinking about it, I was like, oh, like, there are so many different ways that this could go. On one hand, it was a very like important topic to me, but also maybe too big of a topic. And so, I actually, like, kind of put that on the back burner. And it wasn't until later when I connected it to another...it wasn't necessarily different at all, but just, like, an extension of this idea is, oh, like, people are struggling with coupling in tests or, like, it manifests in tests. And so, I thought maybe that could be the angle that I took on this topic that kind of gave me a little bit more focus. And I didn't even end up saying like, "Yeah, this talk was, like, born out of just, you know, wrestling with coupling or anything like that." So, it's cool, to me, that you picked up on it as a theme because it was...I had, you know, ended up not being super explicit about it, but it was certainly, like, a thing that was driving the content from my perspective. JOËL: Interesting. So, it started as a coupling talk and then got sort of focused through the lens of testing. STEPHANIE: Yeah. And I think there was a part of me that was like, you know, I don't know if I could just teach the concept of coupling, like, by itself without the framing of testing for people who this is, like, a new concept for them. I realized that maybe it would be more effective to be like, "Hey, like, have you experienced test pain? You know, have you had to mock out a billion objects or changed, you know, made one change and then had to fix, like, a million tests subsequently? Then this talk is for you." And then weave in the idea of coupling in it to kind of start to help people feel familiar with it or just, like, identify it without as much, like, jargon as kind of I've seen when I've tried to figure out, like, how to manage it. JOËL: It's interesting because I think it gives you a, like, concrete, valuable thing to optimize for as opposed to, like, hey, let's lower coupling because then you're writing, you know, quote, unquote, "better code." And you get to feel better about yourself as a programmer because you're doing things the, quote, unquote, "right way." That's very kind of hand-wavy, and I think sometimes leads people down a bad path where they're optimizing things that they shouldn't be. But the tests give you this very concrete way to say, "Hey, we're not just trying to reach the, like, low score record for the app in terms of coupling. We're trying to reduce test pain. Tests are painful. And that pain is telling us something. It's telling us that we've crossed some sort of threshold for coupling. Let's find ways to reduce it, not so that we can feel good about ourselves, but so that our tests are actually manageable." STEPHANIE: Yeah, I am really glad you picked up on that, too, because I feel the exact same way when someone just tells me to decouple something or, like, makes a note that, like, oh, this feels really coupled. I don't know what that means necessarily. And it's not very convincing to just be like, "Oh, you should write loosely coupled code [laughs]," at least for me. What you said just now, it's like, it's not to feel good about ourselves, you know, to write code that way, but, actually, to just feel good about our code, period [laughs]. And, yeah, finding that validation through just, like, actually working with code that is easier to change that is the goal, not necessarily to, yeah, kind of pursue some totally subjective, like, metric. JOËL: So, one of the kinds of coupling that you called out, I think, was where you hardcode a class name of some other class in your object. And that feels, like, really sort of innocuous. Like, of course, my objects can talk to other objects. And maybe I want to, like, refer to a class somewhere. Why is that such a like tricky piece of coupling to work with? STEPHANIE: It's not necessarily intentional sometimes. Like, you just do it because you're like, well, I need access to this class somewhere, and I happen to already be in this file. So, why not just hard-code it here? I do think it's a little tricky because the file that you're writing might be, like, very far down in, like, your code flow or, like, your code path, like, very far from, like, a controller or any kind of entry point into your system, at least based on what I've seen in a lot of modern Rails apps. And so, I think that coupling gets really, really obscured. I have found that, like, if I have to kind of write a more, like, a higher level test, like, maybe a request spec or something, there are times when I'm, like, having to deal with a lot of classes just to set stuff up in a test like that that I didn't think I would have to [chuckles] when I first went about trying to just be like, oh, like, let's just figure out how to get a 200 response [laughs] from this request. So, you're really burying perhaps the things that are needed to set up, like, that full path of execution. And sometimes, it only comes out when you're writing a test for it. JOËL: And you mentioned briefly, in passing, the idea that oftentimes this sort of coupling manifests as a lot of extra test setup because your object that you're trying to test now also needs all these other things that are related in order to be tested. But sometimes even when you hard code a class, though, you can't even just say, "Oh, I want this particular user or something returned." So, you have to then do something like allow this class to receive class method and return, and now you're stubbing. And I don't know how you feel about stubs in RSpec. I always treat them a little bit like a code smell in the like classic sense of it's not necessarily bad, but maybe pause, take a look, and ask yourself, "Why is that there, and should I do things differently?" STEPHANIE: Yeah. I ended up having, like, a lot of examples of stubbing in my example because the code had just been set up where that was the only way that you could access those collaborators, essentially, to, like, make an assertion on them, or have them do something different because you actually needed to go into a different path, right? And I was like, yeah, this should feel weird. You should feel a little bad [laughs] or at least, you know, kind of just pay attention to that feeling, even if you can't really do anything about it in that particular instance. But on the flip side, you know, it's like, yes, it feels a bit strange, you know, but it's not all bad, right? Like, you're kind of learning like, oh, hey, like, I am coupled to this hard-coded class because I am needing to stub, like, a class method that returns it, or that constructs it. And at least you've exposed that, you know, for yourself. One thing that I was running into a lot in my example, too, was that those things, like, weren't obvious when you were just reading maybe, like, the public methods and trying to figure out what was happening in them because they were wrapped in private methods. I was a little bit conflicted about this because there were times when it was already just a single method call, but then it was just kind of wrapped in a private method that actually hid [laughs] the things, like all the dependencies that were passed as arguments. And I found that to be, sure, it looks kind of cleaner. But then all you need to do is scroll down [laughs], and then you're like, oh, actually, there's all these other things involved, but it was kind of hidden away for me. And I found that, actually, like, at least when I actually needed to change things, less helpful than I imagine what the, you know, code author intended. Do you have any thoughts about hiding details like that? JOËL: I'm kind of a big fan. STEPHANIE: Hmmm. JOËL: The general idea, I think, is called the single level of abstraction principle. Whatever sort of public method that you're calling is often implemented in terms of...let's say it does a few different things. It's implemented in terms of, like, these sort of high-level concepts. So, whoever is reading the public method doesn't need to like care about the details of how each step is implemented. So, maybe you're fetching something from an API, and then you're making a database call, and then you're doing some transformation and creating some new objects from it. Having all of the, like, HTTP calls and the ActiveRecord stuff and the, like, transformation all in the public method, yes, there's a lot of complexity happening there, and it makes that obvious. But it also makes it really hard to get a sense of what is happening. So, I like to say, "Hey, there are four steps. Let's wrap them all each in a private method then you can call all of those in the public method." The public method now sort of reads like a very simple sort of script. First, fetch data from the HTTP API, then fetch some data from the database, then apply this transformation, then create this object. And if I'm mostly caring about what this object does and not the how let's say I'm building some other objects that interact with this, that is the information I want to know. Where I care about the actual implementation of, oh, well, exactly how is the ActiveRecord stuff done when I'm doing internal changes to the object, that's when I care about those private methods. I think where it gets tricky, and I think that's the point that you were bringing up, is that if you write code in that way, it has to change the heuristics of how you read code to detect complexity. Because, oftentimes, I think a very classic heuristic for code complexity is just line length. If you have a 50-line method, probably there's a lot of complexity there. Maybe there's a lot of coupling. If it's a four-line method that is written at a high level of abstraction that just calls out to private methods, you scan over. You're like, oh, nice and clean. Nothing to see here. Move on. And so, that heuristic doesn't really hold up in a codebase where you're applying this single level of abstraction. Do you think that lines up with your experience? STEPHANIE: Hmm. As I was listening to you, I was like, yeah, like, that makes total sense to me. But then I also clearly disagreed a little bit [laughs] in my initial...kind of what I was saying initially. And I think it's because that single layer of abstraction was not very well defined. JOËL: Hmm. That's fair. STEPHANIE: Yeah. Where, in fact, it was actually misleading. Like, it wanted to be at that level of abstraction, but it really wasn't. Like, it was operating on things at, like, a lower level and wasn't designed with that kind of readability in mind. So, it was more, like, it was just hiding stuff a little bit, at least for me. And, I think, it certainly would have taken, like, more work to figure out what that code, like, really was meant to convey. It might have taken some refactoring to coalesce at that single level. And that was essentially kind of what I was showing in my talk as, like, how to get to saying, like, "Hey, we actually are operating in the lower level, but I don't think we need to." There was some amount of, like, looking at all of the how to figure out, like, oh, maybe these things we don't even need to expose in this class. And we kind of got to a place where those details weren't, like, needed in that class at all. So, it's one of those things where it's harder than it sounds [laughs]. JOËL: It's definitely an art. STEPHANIE: Yeah. JOËL: And I think what you're saying about some of the coupling being, like, scattered throughout the class, it's something that I see a lot with situations where you're coupled, not so much to, like, a single class, but to something side effectful. So, you're building some kind of integration with a third-party API, and you're going to have to make a lot of HTTP calls. And each of those might be individually simple, and they're all sort of maybe in different private methods or whatever, or they're interspersed among a larger chunk of logic. And that makes your tests really complicated. But there's no, like, one place you can point at and be like, ooh, that's the one place where there's a lot of complexity. What's happening here, though, is that your business object that's doing stuff is coupled to the network, and that coupling is going to force you to do some stubbing. It's going to force you to deal with a bunch of side effects that are non-deterministic in your code. And you used the word coalesce earlier that I really liked because I think that's often a situation where you do have to stand back and say, "Look, there's a lot of HTTP going on here. What if I coalesced it all into an object? Now I have two objects: one that's responsible for business logic, and one that's responsible for just the HTTP calls." And, all of a sudden, the tests just totally simplify. And we've removed some coupling, but that's not something that you would have seen just from reading the code. Because, as you were saying, it's sort of scattered in little bits and pieces throughout your file that don't necessarily catch your eye. STEPHANIE: Yeah. Which brings me to a blog post that I had found a lot of inspiration from in the talk that I'll link. It's called "That One Thing: Reduce Coupling for More Scalable and Sustainable Software." But it's actually about tests [laughs], even though it doesn't make an appearance in the title of the blog post at all. But this is where I kind of got the idea of necessary versus unnecessary coupling in test. Because I had never thought about how, yeah, like, when you write a test, you are very correctly coupling yourself to at least the method and class under test [laughs], if not also the arguments, right? Or anything else needed to construct what you're testing. And literally having that listed out for me in this blog post I think it's a...they use some examples in Java. And so, there's, like, a little bit more [laughs] setup involved. But I think they're like, yeah, these are six things that, like, it's mostly fine if you're coupled to these because that's kind of what needs to happen in a test. But, like, even having something to compare a test I wrote to just, like, okay, these are the things I know I need. And then, you can start to see when you've diverged from that list, when you are finding yourself coupled to some internals of your class. I really...that was actually, like, really helpful for me because, as we talked about earlier, like, it can be kind of communicated so abstractly. But here is, like, a very clear heuristic for when you should at least, like, start to pay attention or be like, oh, this is something that was needed to get the test to run but is now starting to feel a little unnecessary because it's not on this list. JOËL: That list reminds me, or the idea of a list of things to check out for when thinking about coupling, reminds me of the concept of connascence, which is a fancy word for almost a, like, categorization of different types of coupling because coupling comes in different flavors, some of which are tighter forms of coupling than others. And so, having that vocabulary has been really helpful for me when I'm looking at PRs and code review, or even when I'm refactoring my own code. Kind of like that list that you mentioned that you have, now I have some heuristics to look at that and say, "Oh, can I go from a connascence of position to a connascence of naming, and does that help me?" STEPHANIE: Yeah, I like that you mentioned the positional connascence because I also came across a really great metaphor for kind of things that need to change together, like, when that makes sense. And it was basically the idea of a dishwasher and a laundry machine [laughs]. I wish I could recall, like, what book this was from. But it was basically like, oh yeah, like, in theory, you're washing two things. So, maybe they are similar, but then you're like, no, actually, you want these to be a little bit separate because, you know, you don't want to wash your dishes and your clothes in the same machine. I don't know, maybe that exists [laughs], but I don't think it would do a very good job for either goal. And I think that was really helpful, for me, in imagining, like, the difference between kind of coupling and cohesion, like things that...even just imagining, like, kind of where I'm doing those things in the house, right? It's like, okay, that lives in a separate room. And, like, the kitchen is for the dishes, and that could be like, you know, a module if you will. And, like, laundry happens in the laundry room, and how to kind of just separate those things, even though they also do share some qualities, too. Like, they're both appliances, right? And so, that's the way that they are similar, but they're not the same. JOËL: You just mentioned the sort of keyword cohesion. And for our listeners who are not familiar with that term, it refers to an object sort of having one thing that it does well. Like, everything in that class sort of works towards the same goal, kind of similar to the idea of the single responsibility principle. So, in my earlier example, where we're sort of interspersing some business logic, a lot of HTTP requests, and pulling out an object that's focused on HTTP, like everything is based around that, now that object has higher cohesion because it's all doing one thing. So, if you read classic object-oriented literature, the recommendations that you'll typically see are that objects should have high cohesion and low coupling. STEPHANIE: Yeah. Think of a dishwasher and a washing machine next time [laughs] you come across something like that. Because I feel like those are really great, like, real-life examples of that separation. JOËL: Did you go to Jared Norman's talk on the third day: "Undervalued: The Most Useful Design Pattern"? STEPHANIE: No, I didn't. Can you tell me about it? JOËL: It felt like he was addressing a lot of the same themes as you were but from more of a code perspective than a test perspective. Talking a lot about, again, forms of coupling, dependencies, and then, specifically, one of the tools that he focused on to reduce the coupling that we see is value objects and factory methods to construct those. So, for any of our listeners who, when the talks come out, watch Stephanie's talk and are like, "Wow, I would love to learn more about this," a great follow-up, Jared Norman's talk: "Undervalued: The Most Useful Design Pattern." STEPHANIE: Yeah, that's neat because I can see that being a solution to the hard code did class names that we were talking about earlier. And I like how that is kind of, like, a progressive lesson in coupling a little bit. I'm really glad you shared that talk with me because now I'm excited to watch it when it comes out. And in general, I just love learning new vocabulary or finding new ways to speak about this topic with clarity. So, if any of our listeners have just additional mental models for coupling [laughs] different metaphors, different household appliances [laughs], or something like that, I would love to know. JOËL: You would like that, given that our first episode together was about "The Value Of Specialized Vocabulary." STEPHANIE: Yeah, it's clearly undervalued. JOËL: Haha, I see what you did there. STEPHANIE: Thank you. Thank you very much [laughs]. JOËL: On that terrible/wonderful pun, shall we wrap up? STEPHANIE: Let's wrap up. Show notes for this episode can be found at bikeshed.fm. JOËL: This show has been produced and edited by Mandy Moore. STEPHANIE: If you enjoyed listening, one really easy way to support the show is to leave us a quick rating or even a review in iTunes. It really helps other folks find the show. JOËL: If you have any feedback for this or any of our other episodes, you can reach us @_bikeshed, or you can reach me @joelquen on Twitter. STEPHANIE: Or reach both of us at hosts@bikeshed.fm via email. JOËL: Thanks so much for listening to The Bike Shed, and we'll see you next week. ALL: Byeeeeeeee!!!!! AD: Did you know thoughtbot has a referral program? If you introduce us to someone looking for a design or development partner, we will compensate you if they decide to work with us. More info on our website at: tbot.io/referral. Or you can email us at: referrals@thoughtbot.com with any questions.

    vision detroit mvp dragons tests dungeons api turbo java javascript rails prs mandy moore coupling conversing d beyond quenneville railsconf activerecord rspec stephanie you stephanie yeah stephanie how stephanie it stephanie thank stephanie no stephanie oh action cable
    426: Bringing "Our Selves" to Work

    Play Episode Listen Later May 14, 2024 33:04


    Joël shares his preparations for his RailsConf talk, which is D&D-themed and centered around a gnome character named Glittersense. Stephanie expresses her delight in creating pod-related puns within thoughtbot's internal team structure, like "cross-podination" for inter-pod meetings and the adorable observation that her pod resembles "three peas in a pod" when using the git co-authored-by feature. Together, Stephanie and Joël discuss bringing one's authentic self to work, balancing personal disclosure with professional boundaries, and fostering psychological safety. They highlight the value of shared interests and personal anecdotes in enhancing team cohesion, especially remotely, and stress the importance of an inclusive culture that respects individual preferences and boundaries. Transcript: We're excited to announce a new workshop series for helping you get that startup idea you have out of your head and into the world. It's called Vision to Value. Over a series of 90-minute working sessions, you'll work with a thoughtbot product strategist and a handful of other founders to start testing your idea in the market and make a plan for building an MVP. Join for all seven of the weekly sessions, or pick and choose the ones that address your biggest challenge right now. Learn more and sign up at tbot.io/visionvalue. STEPHANIE: Hello and welcome to another episode of The Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I'm Stephanie Minn. JOËL: And I'm Joël Quenneville. And together, we're here to share a bit of what we've learned along the way. STEPHANIE: So, Joël, what's new in your world? JOËL: So, at the time of this recording, we're recording this the week before RailsConf. I've been working on some of the visuals for my RailsConf talk and leaning on AI to generate some of these. So, my talk is D&D-themed, and it's very narrative-based. We follow the adventures of this gnome named Glittersense throughout the talk as we learn about how to use Turbo to build a D&D character sheet. And so, I wanted the AI to generate images for me. And the problem I've had with a lot of AI-generated images is that you're like, okay, I need a gnome, you know, in a fight doing this, doing that. But then, like, every time, you get, like, totally different images. You're like, "Oh, I need an image where it's this," but then, like, the character is different in all the scenes, and there's no consistency. So, I've been leaning a little bit more into the memory aspect of ChatGPT, where you can sort of tell it, "Look, these are the things. Now, whenever I refer to Glittersense, whenever you draw an image, do it with these characteristics that we've established what the character looks like." Sometimes I'll have, like, a text conversation kind of, like, setting up the physical characteristics. And then, it's like, okay, now every time you draw him, draw him like this, or now every time you draw him, draw him with this particular piece of equipment that we've created. And so, leaning into that memory has allowed me to create a series of images that feel a little bit more consistent in a way that's been really interesting. STEPHANIE: Cool. Yeah, that makes sense because you are telling a story, right? And you need it to have a through line and the imagery be matching as you progress in your presentation. I actually don't know a lot about how that memory works. Does it persist across sessions? Do you have to do it all in one [laughs] go, or how does that work? JOËL: So, there's, like, a persistent chat. So, you can start sort of multiple conversations, but each conversation is its own thread with its own memory. And it will sort of keep track of certain things. And sometimes I'll even say, "Hey..." instead of, like, prompting it for something to get a response, you could prompt it to add things to its memory. So say like, "From now on, when I ask you these types of questions, I want you to respond in this way," or, "From now on, when I ask you to generate an image, I want it done in this format." So, for example, RailsConf requires all of their slides to be 16 by 9. If I want, like, a kind of cover image or, like, something full-screen, I need an image that is 16 by 9. So, one of the things I prompt the AI with is just, "From now on, whenever you generate an image, give me an image in 16:9 aspect ratio." STEPHANIE: Cool. I also was intrigued by your gnome's name, Glittersense. And I was wondering what the story behind that character is. JOËL: The story behind the name is that I was playing D&D with a friend who was this very kind of eclectic Dragonborn character. And I did some sort of valiant deed and got the name Glittersense bestowed upon me by this Dragonborn for having helped him out in some, like, cool way. So, that's a fun name. And so, when I was searching for a name for my character in this talk, I was like, you know what? Let's bring back Glittersense. I like that. I think it captures a little bit of, like, the wonder and the whimsy of a gnome. STEPHANIE: That's really cute. I like that a lot. JOËL: So, Stephanie, what's been new in your world? STEPHANIE: So, lately, I've been having a lot of fun with coming up with names of things. You know the saying how naming is one of the hardest things in software? Well, okay, I'm not actually going to talk about anything that I named very particularly well in my code, but I've been just coming up with a lot of puns. It's just, I don't know, my brain is kind of in that space. And one thing that...I can't recall if I have talked about this on the show before, but our team at thoughtbot is experimenting with kind of smaller sub-teams within it called pods. We have now kind of been split into pods with other people who are working on maybe similar client projects. I have been having some really good naming ideas around [laughs] pod-related puns. So, one thing that we did as part of this experiment was setting up meetings for pods to meet each other, and spend time together, and kind of share what each other was up to. And I was the first to coin the term cross-podination, kind of like cross-pollination. And I think I just, like, said it offhand one day, and then it caught on. And I was very pleasantly surprised to see that people just leaned into it and started naming those meetings cross-podination meetings. And then, another one that came about recently was my pod there's three of us in it, and we were pairing, or I guess it's not really called a pairing if there's three. We were mobbing or ensembling, whatever you want to call it. And sometimes we like to use the git co-authored-by feature where you can attribute, you know, commits to people that you worked on them with. And in GitHub when you, you know, add people's emails to the commit, you know, you see your little GitHub profile picture in a little circle. And when you have multiple people shared on a commit, it is just, like, squished together. And since we're a trio, I was like, "Oh, it's like we're, like, three peas in a pod." JOËL: [chuckles] STEPHANIE: And I realized that it was an excellent missed opportunity for our pod name. We're something else. But I am hereby reserving that name for the next pod that I am in. You heard it here first [laughs]. It looked exactly like just three snug little peas. And I, yeah, it was very cute. I was very delighted. And yeah, that's what's new for me. JOËL: I'll also point out the fact that you are currently talking on a podcast. STEPHANIE: Whoa, whoa. So, you and I are a pod [laughter]. We're a podcasting pod [laughs]. Wow, I didn't even think about that. My world is just pods right now [laughs], folks. JOËL: How do you feel about puns as an art form? STEPHANIE: [laughs] Wow, art form is a strong phrase to use. I don't hate them. I think it depends. Sometimes I will cringe, and other times I'm like, that's great. That's excellent. Yeah, I think it depends. But I guess, clearly, I'm in my pun era, so I've just accepted it. JOËL: Are you the kind of person who is, like, ashamed but secretly proud when you make a really good pun? STEPHANIE: Yeah, that's a very good way to describe it. I'm sure there are other people out there [laughs]. JOËL: What's interesting with puns, right? Like, some people love them, some people hate them. Some people really lean into them, like, that becomes almost, like, part of their personality. We had a former teammate who his...we made a custom Slack emoji with his face, and it was the pun emoji because he always had a good pun ready for any situation. And so, that's sort of a way that I feel like sometimes you get to bring an aspect of your personality or at least a persona to work. What parts of yourself do you like to bring to work? What parts do you like to maybe leave out? STEPHANIE: Yeah, I am really excited about this topic because I feel like it's a little bit evergreen, maybe was kind of a trendy thing to talk about in terms of team culture in the past couple of years, but this idea of bringing an authentic or whole self to work as, like, an ideal. And I don't know that I totally agree with that [laughs] because, like you said, sometimes you have a different kind of persona, or you have a kind of way that you want to present yourself at work. And that doesn't necessarily mean it's a bad thing. I personally like some kind of separation in terms of my work self and my rest of life self [laughs]. Yeah, I just think that should be fine. JOËL: So, you might secretly be the pun master, but you don't want your colleagues to know. STEPHANIE: [laughs] That's true. Or I save my puns only for work [laughter]. If I ever have, like, a shower thought where I think of a really good pun, I will, like, send a Slack message to myself to find [laughs] the perfect opportunity to use this pun in a meeting [laughs]. I don't actually do that, but that would be very funny. JOËL: I feel like there's probably a sense in which nobody is a hundred percent their authentic self or their full self in a work situation, you know, it varies by person. But I'm sure everybody, to a certain extent, has a professional persona that they inhabit during work hours. STEPHANIE: Yeah, and I like that the way we're talking about it, too, is a professional persona doesn't necessarily mean that you're just a little...matching kind of a business speak bot [laughs], where it's kind of devoid of personality, but just using all the right language in their emails [laughs] and the correct business jargon or whatever. To me, what is important is that people are able to choose how they show up or present themselves at work. That's, like, an active choice that they're making, not out of obligation or fear of consequences. You know, like, it's fine to be a little more private at work if that's just how you want to operate. And it's also fine to be more open about sharing things going on in your personal life. Because I've seen ways in which both have been more enforced or, like, there's pressure to perform one way or another. And that could mean, like, when people kind of encourage others to try to be more of themselves or, like, share more things about personal life. That's not always necessarily a good thing if it's not something that people are comfortable with. And I suspect that we have kind of pulled back a little bit from that, but there was certainly a time when that was a bit of an expectation. And I'm not sure that that was quite [chuckles] what we wanted to aim for in terms of just the modern workplace. JOËL: It is interesting because I think there can be some advantages to maybe building connection with people by sharing a little bit more about your life. But, again, if there's pressure to do it, that becomes really unwholesome. STEPHANIE: Yeah. Unwholesome is a good word to use. Like, I want that wholesome content [laughs] at work. And I actually have a couple of thoughts about how I prefer to share, like, just personal things with my team members. And I'm curious kind of where you fall on this as well. But a couple of things that our team does that I really like is we have a quarterly newsletter that one of our team leads puts together. She has an open call for submissions, and people just share any, like travel plans, any professional wins, any kind of personal life things that they want to share. People love talking about their home improvement adventures [laughs] on our team, which is really fun. And yeah, like, just share photos and a little blurb about what they've been up to. And this happens every quarter. And it's always such a delight to remember a little bit like, oh yeah, my co-workers have lives outside of work. But I really like that it's opt-in and also not that frequent, you know? It's kind of like, this is the time to share any like, special things that have happened in the past three months. And yeah, I think every time a new dispatch of it comes out, everyone kind of gets the warm and fuzzy feelings of appreciating their co-workers and what they've been up to. JOËL: Do you think that that kind of sharing sort of maybe helps personalize a little bit of our colleagues, especially because we're all remote and we're interacting with each other through a screen? STEPHANIE: Yes. Yeah. That's another good distinction. I think it is, like, a little more important that there are touch points like these when we are working remote because, yeah, the water cooler conversation just doesn't really happen nearly as much as it does when you're in an office. And I feel like that's the kind of thing that I would talk about at the water cooler [laughs]. It's like, "Oh yeah, I went to Disney World, or traveled for this conference, or I built new garden beds for my yard," just stuff like that. I don't know, I don't find that...like, when you're just communicating over Slack and email, there's not a good place for that kind of stuff. And that's why I really like the newsletter. JOËL: One thing that's interesting about the difference between in-person and remote is that, in person, a way that you can express personality in the office is you can do some things with your workspace. You might have some items on your desk that are of personal interest. And, you know, you might still do that when you're working remote, but those don't get captured by your webcam unless it's in your background. Your background you can get real creative with. But you can also, like, really curate that to, like, show practically nothing. Whereas if you were putting things on your desk in the office, there's kind of no way for your colleagues not to see that. So, you had to be...like, it had to be things that you were willing for everyone to see. But at the same time, sometimes it's nice to be able to say, hey, I'm going to put a touch of, like, things that are meaningful to me in my work life. STEPHANIE: Yeah, I really like that. I mean, Joël, your background is always these framed maps on the wall, hanging on the wall, and that is very you, I think. Did you kind of think about how they'll just be your background whenever you're in a meeting, or they just happened to be there? JOËL: So, these I had set up pre-pandemic. I like the décor. And then, when I started working from home in 2020, I was trying to figure out, like, where do I want to be to take meetings? And I was like, you know what? The math wall is pretty cool. I think that's going to be my background. I guess now it's almost become, like, a bit of a trademark. STEPHANIE: Yeah, I feel that. My trademark...I have a few because I like to move around when I take meetings. So, when I'm at my desk, it's the plants in my office. When I'm in my kitchen, it's either my jars [laughs]. So, I have, like, open shelving and just all of these jars of, you know, some of it is ingredients like nuts, and grains, and stuff like that, and some of it is just empty jars that I use for drinking water. So, I have my jar collection. And then, occasionally, if I'm sitting on the other side of the table [chuckles], all of my pots and pans are hanging in the background from above my stove. So, yeah, I'm the jars, pots, and plants person [laughs] at the company. JOËL: You know, we were talking earlier about the idea that it's harder to see your sort of workspace in a remote world. And I just remembered that we do a semi-regular...there's, like, a thread at thoughtbot where people just share pictures of their workspace, and it's opt-in. You don't have to put anything in there. But you get a little bit of, like, oh, the other side of the camera. That's pretty cool. STEPHANIE: Yeah, I love seeing those threads. And I think a lot of people in our industry are also gear nerds, so [laughter] they love to see people's, like, fancy monitor and keyboard setups, maybe some cool lighting, oh, like, wire organization [laughs]. JOËL: Cable management. STEPHANIE: Yep. Yep. Those are fun. And I actually think another one that we've lost since going remote is laptop stickers because that was such a great way for people to show some personality and things that they love, like programming stuff, maybe, like, you know, language stickers or organizations like thoughtbot stickers, too, and also, more personal stuff if they want. At a previous company, we were also remote, and someone came up with a really fun game where people anonymously submitted pictures of their laptop stickers. And we got together and tried to guess whose laptop belonged to who just based on the stickers. JOËL: Oh, that's fun. STEPHANIE: Yeah, that was really fun. I keep forgetting that I wanted to organize something like that for thoughtbot. But now I'm just thinking about it, and I feel the need to decorate my laptop with some stickers after this [laughs]. JOËL: One thing I do want to highlight, though, is the fact that several years back, when people were talking a lot about the importance of bringing your sort of authentic or whole self to work, one of the really valuable parts of that conversation was giving people the ability to do that, not forcing people to sort of hide parts of themselves, especially if they don't fit into a dominant culture or demographic, in order to be able to even function at work, right? That's a sort of key aspect of, I guess, basic inclusivity. And so, I think that's still a hundred percent true today. We want to build cultures that are inclusive, both in our in-person professional situations and for remote teams. STEPHANIE: Yeah, 100%. I think, for me, what I think is a good measure of that is, you know, how comfortable are people disagreeing at the company kind of in public or sharing an alternative perspective? Like, that should be okay and celebrated, even, and considered, you know, with equal weight as kind of what you're saying, the dominant identity or even just opinion. Like, especially in tech, I think people have very strongly held opinions, and when they're disagreed with...I've become a little skeptical of the idea of, like, this is how we do things here or, like, we don't do that. And I think that rather than sticking to a, like, stance like that, there's always room to incorporate, like, new approaches, new perspectives, new ways of thinking to a given problem. And that can only happen when people are comfortable with going there, you know, and kind of saying, like, "This is important to me," or, like, "This is how I feel about it." And that, in and of itself, is just equally valid [laughs] as whatever is taking the airtime currently. JOËL: That's really interesting because I feel like now you've leaned into almost the idea of psychological safety for a team. And if you're having to sort of repress or hide elements of the way you think, or maybe even sort of core elements of your identity to fit in with a team, that's not psychologically safe, and you can't have those deeper conversations. STEPHANIE: Yeah, 100%. I think it's two sides of the same coin, you know, it's like two ways of saying the same thing, that people should be able to conduct themselves in the way they choose to [laughs]. And I can't imagine anyone really disagreeing with a statement like that. JOËL: So, I know you choose to not always share everything about your life or sort of...I don't want to say bring your authentic self but, like, bring everything about yourself to the workplace. Do you have a sort of a heuristic for what you decide to share or not share? STEPHANIE: Yeah. I don't know if it's necessarily a heuristic so much as it's just what I do [laughs]. But I tend to do better with, like, smaller groups, and, actually, that's why I think pods has been working really well for me personally because I can share personal information just in a more intimate setting, which is helpful for me. And yeah, I tend to, like, find once, like, either Slack channels or Spaces, meetings are starting to get into the, like, 10, 11, 12 people territory is when I hold it back a little bit more, not because of any sort of, like, reason that I don't want to share. It's just, like, that's just not the venue for me. But I do love when other people are, like, open, even in, like, larger spaces like that. I appreciate when other people do it just to, you know, signal that it's okay [laughs]. And I enjoy throwing a reaction or responding in a thread about, you know, something that someone shared in a bigger channel. And I think that diversity is actually really helpful because it conveys that, like, there's different ways of existing online in your work environment and that they're all acceptable. What about you? How do you kind of choose where to share things about your personal life? JOËL: I think, kind of like you, I don't really have a heuristic. I just sort of go with gut feeling. I think I, sort of by nature, have always been maybe a little bit of having, like, separate professional and personal lives and keeping those a little bit more distinct. And, you know, there's some things that kind of cross over, like, oh, you know, I tried out this fun, new restaurant, or I did a cool activity over the weekend, or something like that. I think I've come to see that there can be a lot of value in sharing parts of yourself with other colleagues. And so, from time to time, I'll, like, maybe bring in something a little bit deeper. And, like you said, sometimes that's more easily done in a smaller context. And then yeah, for some things, it's like, okay, I'm going to share photos from a vacation in that, you know, quarterly newsletter. That's kind of fun. But also knowing that there's no pressure that's nice. STEPHANIE: Yeah. I think you're really good about finding the right avenues for that. I like, love when you show photos in the travel channel, even though I have that channel muted [laughs]. You'll, like, send me the link to the post in that channel. And yeah, I love that because it's a way for you to kind of, like, find the right place for it, and then also share it with any particular people if you choose to. JOËL: I think, also, personal connections can be a way to build deeper relationships, especially in smaller groups. And you can form deeper connections with colleagues over a particular project, or a particular technology, or a tech topic, or, you know, just a passion about mechanical keyboards, or something like that. But if you're people who chat kind of more on the regular for different things, maybe separate from a client project you're on or something like that, and you do find yourself exchanging a little bit more about, oh, you know, what you're doing in your life, or what are the things that are going on for you, that often does tend to build, I think, a deeper connection between colleagues, which can be really nice. STEPHANIE: Yeah. And I like that those relationships can also change. Like, there's different seasons in which you're more connected to some people and then less connected. Sometimes a colleague that you have shared interests with becomes someone that you kind of are in touch with more regularly, and then maybe you switch projects, and you aren't so much kind of as up to date. But, I don't know, I always think that there's, like, the right time for that kind of stuff, and it emerges. JOËL: I'm going to throw a bit of a buzzword at you, and I'd love to get your reaction. The idea of belonging, the feeling of belonging on a team, is that a good thing, something that we should seek out? And if so, how much of that is responsibility of, like, management or, like, a property of the team or the group to make you sort of feel that belonging? And how much of that is on you having to maybe disclose things about yourself or share a little bit of your personal life to, like, create that sense of belonging? STEPHANIE: Whoa. Yeah, that is a good way to frame it. I think there's a balance. There've been some, like, periods of my work life where I'm like, oh, I need more of a detachment from work and other times where I'm like, oh, I feel really disconnected, like, I want to feel like more of a part of this team. But I do think it's a management responsibility. And one thing that I know people to be cautious of is, you know, becoming too close at work. I don't know if your work being treated like a family, like, that kind of language can be a little bit borderline. JOËL: Almost manipulative. STEPHANIE: Right. Yeah, exactly. I do think there's something to be said about community at work and feeling like that kind of belonging, right? But also, that you can choose how much, like, you want to engage with that community and that being okay. I don't think it necessarily needs to be only through what you share about yourself. Like, you can have that sense of connection just by being a good colleague [chuckles], right? Like, even if the things you talk about are just within the realm of the project you're working on, like, there's still a sense of commitment and, yeah, in that relationship. And I think that is what matters when it comes to belonging. In the past, ways that I've seen that work well in regards to kind of how you share information is just, like, I don't know, share how you're doing. Like, you don't have to provide too many details. But it could be like, "Oh, I'm kind of distracted in my personal life right now, and that's why I wasn't able to get this done." People should be understanding of that, even if you don't kind of let them in on the more personal aspects of it. JOËL: Right. And you don't have to give any details, right? STEPHANIE: Yeah. JOËL: You should be in a place where people are comfortable with not knowing and not be like, "Ooh, what's going on with Stephanie's life? " STEPHANIE: [laughs] Yeah. But I do also think, like, the knowing that, like, something is going on is, like, also important context, right? Because you don't necessarily want that to impact the commitments you do have at work. JOËL: Right. And people tend to be a little bit more understanding if you're having to maybe shift some meetings around, or if you're struggling to focus on a particular day, or something like that. STEPHANIE: Yeah. 100%. JOËL: Yeah, we should normalize it of just like, "Hey, I'm having a hard day. I don't want to give details, but you know." STEPHANIE: Yeah. Yeah. I think a way that that is always kind of weird is how people communicate they're taking a sick day [laughs]. I actually had someone tell me that they really appreciated a time when I just said, "You know, I need to take care of myself today," and didn't really say anything else [laughs] about why. Because they're like, "Oh, like, that helped normalize this idea that, like, that is fine just kind of as is." There's no need to, you know, supply any additional reasoning. JOËL: Sometimes I feel like people almost feel the need to like, justify taking sick time. So, you've got to, like, say just how bad things are that now I'm actually taking sick time. STEPHANIE: Yeah, which is...that's not the point, right? You know, we have it because we need it [laughs]. So, yeah, I'm glad you mentioned that because I think that's actually a really good example of the ways that people, like, approach kind of bringing themselves to work like that. JOËL: Yeah, sometimes it's setting a boundary. An aspect I'm curious to look at is you, and I do a little bit of this with this podcast, right? Every week, we share a little bit of what's new in our world, and it goes out into the public internet. How do you tend to pick those topics and, like, how personal are you willing to get? STEPHANIE: Yeah. Oh, that's so hard. It's always hard [laughs], I think. I generally am pretty open. You know, I have talked about plans that I have for moving. I don't know, things about my gardening. I think I've also been a little vulnerable on the show before when I've, like, had a challenge, like, at work. But yeah, it's important, to me, I think, to be, like, true. Like, I think part of what our listeners like about this show is that we show up every week, and it's just a chat between two friends [laughs]. JOËL: Uh-huh. STEPHANIE: It also is kind of weird to know that it's just, like, out there, right? And I don't really know who's listening on the other side. I do know that, like, a lot of my friends listen. And, in some ways, I like to think that I'm talking to them, right? But yeah, sometimes I think about just, like, in a decade [laughs], it will still be out there. And on one hand, I think maybe it's kind of cool because I can listen back and be like, oh, like, that's what was going on for me in 2024. And other times I'm like, oh my God, what if I'm one day just, like, deeply embarrassed by things I've talked about on this show [laughs]? But that's a risk, I guess, I'm willing to take because I do think that the sense of connection that we foster with our audience is really meaningful. And it gives me a lot of joy whenever I meet a listener who's like, "Oh, you, you know, talked about this one thing, and I really related to it." And yeah, I guess that's what I do this for. What about you? JOËL: Yeah, I think kind of similar to you; tend to talk about things at work, interesting technical challenges, interesting sort of work, or even sometimes client-related challenges. Of course, you know, never calling out any clients by name, you know, talk about some hobbies and things like that. I think where I tend to draw the line a little bit is things that are a little bit more people-oriented in my personal life. So, I tend to not talk about family, and friends, and relationships, and things like that. And, you know, there are some times where there's like, those things intermix a little bit, where I'll, like, have shared, like, "This is what's new in my world." And then, like, off air, I'll follow up with you and say, "So, I didn't tell the whole story on air. STEPHANIE: [laughs] Yeah. JOËL: Here's what actually happened." Or, you know, "Here's this extra anecdote that I wanted you to know, but I didn't want everyone in the audience to hear." STEPHANIE: Yeah. I think the weirdest part for me, too, is I certainly have my, like, parasocial relationships with people that I follow on the internet [laughs], like, people on YouTube, or other podcasts, and stuff like that. But I haven't thought a whole lot about just, like, what that looks like for me as a host of a podcast. I think, kind of the size of the show now it feels right for me, where it's like I run into people who listen at conferences and stuff like that, but it is kind of contained to a work-related thing. So, that feels good because it, I think, for me, helps just give the work stuff a little bit of a deeper meaning, but otherwise isn't spilling over to my regular life. JOËL: And it's always fun when, you know, we get a listener email connecting to, you know, one of the random hobbies or something we've talked about and sharing a little bit of their experiences. I think last spring, I talked about getting a pair of bike shorts and, like, trying it out and seeing how that worked. And a listener called in and shared their experience with bike shorts, and, like, that's a lot of fun. It kind of creates that connection. So, I do enjoy that aspect. STEPHANIE: Yeah. And just to plug, you can write in to us at hosts@bikeshed.fm, and if you have anything you want to share that was inspired by what you heard us talk about on the show. JOËL: We'd love to have you. STEPHANIE: On that note, shall we wrap up? JOËL: Let's wrap up. STEPHANIE: Show notes for this episode can be found at bikeshed.fm. JOËL: This show has been produced and edited by Mandy Moore. STEPHANIE: If you enjoyed listening, one really easy way to support the show is to leave us a quick rating or even a review in iTunes. It really helps other folks find the show. JOËL: If you have any feedback for this or any of our other episodes, you can reach us @_bikeshed, or you can reach me @joelquen on Twitter. STEPHANIE: Or reach both of us at hosts@bikeshed.fm via email. JOËL: Thanks so much for listening to The Bike Shed, and we'll see you next week. ALL: Byeeeeeeee!!!!!!! AD: Did you know thoughtbot has a referral program? If you introduce us to someone looking for a design or development partner, we will compensate you if they decide to work with us. More info on our website at: tbot.io/referral. Or you can email us at: referrals@thoughtbot.com with any questions.

    425: Modeling Associations in Rails

    Play Episode Listen Later May 7, 2024 29:39


    Stephanie shares an intriguing discovery about the origins of design patterns in software, tracing them back to architect Christopher Alexander's ideas in architecture. Joël is an official member of the Boston bike share system, and he loves it. He even got a notification on the app this week: "Congratulations. You have now visited 10% of all docking stations in the Boston metro area." #AchievementUnlocked, Joël! Joël and Stephanie transition into a broader discussion on data modeling within software systems, particularly how entities like companies, employees, and devices interconnect within a database. They debate the semantics of database relationships and the practical implications of various database design decisions, providing insights into the complexities of backend development. Christopher Alexander and Design Patterns (https://www.designsystems.com/christopher-alexander-the-father-of-pattern-language/) Rails guide to choosing between belongsto and hasone (https://edgeguides.rubyonrails.org/association_basics.html#choosing-between-belongs-to-and-has-one) Making impossible states impossible (https://www.youtube.com/watch?v=IcgmSRJHu_8) Transcript: We're excited to announce a new workshop series for helping you get that startup idea you have out of your head and into the world. It's called Vision to Value. Over a series of 90-minute working sessions, you'll work with a thoughtbot product strategist and a handful of other founders to start testing your idea in the market and make a plan for building an MVP. Join for all seven of the weekly sessions, or pick and choose the ones that address your biggest challenge right now. Learn more and sign up at tbot.io/visionvalue. JOËL: Hello and welcome to another episode of The Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I'm Joël Quenneville. STEPHANIE: And I'm Stephanie Minn. And together, we're here to share a bit of what we've learned along the way. JOËL: So, Stephanie, what's new in your world? STEPHANIE: So, I learned a very interesting tidbit. I don't know if it's historical; I don't know if I would label it that. But, I recently learned about where the idea of design patterns in software came from. Are you familiar with that at all? JOËL: I read an article about that a while back, and I forget exactly, but there is, like, a design patterns movement, I think, that predates the software world. STEPHANIE: Yeah, exactly. So, as far as I understand it, there is an architect named Christopher Alexander, and he's kind of the one who proposed this idea of a pattern language. And he developed these ideas from the lens of architecture and building spaces. And he wrote a book called A Pattern Language that compiles, like, all these time-tested solutions to how to create spaces that meet people's needs, essentially. And I just thought that was really neat that software design adopted that philosophy, kind of taking a lot of these interdisciplinary ideas and bringing them into something technical. But also, what I was really compelled by was that the point of these patterns is to make these spaces comfortable and enjoyable for humans. And I have that same feeling evoked when I'm in a codebase that's really well designed, and I am just, like, totally comfortable in it, and I can kind of understand what's going on and know how to navigate it. That's a very visceral feeling, I think. JOËL: I love the kind of human-centric approach that you're using and the language that you're using, right? A place that is comfortable for humans. We want that for our homes. It's kind of nice in our codebases, too. STEPHANIE: Yeah. I have really enjoyed this framing because instead of just saying like, "Oh, it's quote, unquote, "best practice" to follow these design patterns," it kind of gives me more of a reason. It's more of a compelling reason to me to say like, "Following these design patterns makes the codebase, like, easier to navigate, or easier to change, or easier to work with." And that I can get kind of on board with rather than just saying, "This way is, like, the better way, or the superior way, or the way to do things." JOËL: At the end of the day, design patterns are a means to an end. They're not an end in of itself. And I think that's where it's very easy to get into trouble is where you're just sort of, I don't know, trying to rack up engineering points, I guess, for using a lot of design patterns, and they're not necessarily in service to some broader goal. STEPHANIE: Yeah, yeah, exactly. I like the way you put that. When you said that, for some reason, I was thinking about catching Pokémon or something like filling your Pokédex [laughs] with all the different design patterns. And it's not just, you know, like you said, to check off those boxes, but for something that is maybe a little more meaningful than that. JOËL: You're just trying to, like, hit the completionist achievement on the design patterns. STEPHANIE: Yeah, if someone ever reaches that, you know, gets that achievement trophy, let me know [laughs]. JOËL: Can I get a badge on GitHub for having PRs that use every single Gang of Four pattern? STEPHANIE: Anyway, Joël, what's new in your world? JOËL: So, on the topic of completing things and getting badges for them, I am a part of the Boston bike share...project makes it sound like it's a, I don't know, an exclusive club. It's Boston's bike share system. I have a subscription with them, and I love it. It's so practical. You can go everywhere. You don't have to worry about, like, a bike getting stolen or something because, like, you drop it off at a docking station, and then it's not your responsibility anymore. Yeah, it's very convenient. I love it. I got a notification on the app this week that said, "Congratulations. You have now visited 10% of all docking stations in the Boston metro area." STEPHANIE: Whoa, that's actually a pretty cool accomplishment. JOËL: I didn't even know they tracked that, and it's kind of cool. And the achievement shows me, like, here are all the different stations you've visited. STEPHANIE: You know what I think would be really fun? Is kind of the equivalent of a Spotify Wrapped, but for your biking in a year kind of around the city. JOËL: [laughs] STEPHANIE: That would be really neat, I think, just to be like, oh yeah, like, I took this bike trip here. Like, I docked at this station to go meet up with a friend in this neighborhood. Yeah, I think that would be really fun [laughs]. JOËL: You definitely see some patterns come up, right? You're like, oh yeah, well, you know, this is my commute into work every day. Or this is that one friend where, you know, every Tuesday night, we go and do this thing. STEPHANIE: Yeah, it's almost like a travelogue by bike. JOËL: Yeah. I'll bet there's a lot of really interesting information that could surface from that. It might be a little bit disturbing to find out that a company has that data on you because you can, like, pick up so much. STEPHANIE: That's -- JOËL: But it's also kind of fun to look at it. And you mentioned Spotify Wrapped, right? STEPHANIE: Right. JOËL: I love Spotify Wrapped. I have so much fun looking at it every year. STEPHANIE: Yeah. It's always kind of funny, you know, when products kind of track that kind of stuff because it's like, oh, like, it feels like you're really seen [laughs] in terms of what insights it's able to come up with. But yeah, I do think it's cool that you have this little badge. I would be curious to know if there's anyone who's, you know, managed to hit a hundred percent of all the docking stations. They must be a Boston bike messenger or something [laughs]. JOËL: Now that I know that they track it, maybe I should go for completion. STEPHANIE: That would be a very cool flex, in my opinion. JOËL: [laughs] And, you know, of course, they're always expanding the network, which is a good thing. I'll bet it's the kind of thing where you get, like, 99%, and then it's just really hard to, like, keep up. STEPHANIE: Yeah, nice. JOËL: But I guess it's very appropriate, right? For a podcast titled The Bike Shed to be enthusiastic about a bike share program. STEPHANIE: That's true. So, for today's topic, I wanted to pick your brain a little bit on a data modeling question that I posed to some other developers at thoughtbot, specifically when it comes to associations and associations through other associations [laughs]. So, I'm just going to kind of try to share in words what this data model looks like and kind of see what you think about it. So, if you had a company that has many employees and then the employee can also have many devices and you wanted to be able to associate that device with the company, so some kind of method like device dot company, how do you think you would go about making that association happen so that convenience method is available to you in the code? JOËL: As a convenience for not doing device dot employee dot company. STEPHANIE: Yeah, exactly. JOËL: I think a classic is, at least the other way, is that it has many through. I forget if you can do a belongs to through or not. You could also write, effectively, a delegation method on the device to effectively do dot employee dot company. STEPHANIE: Yeah. So, I had that same inkling as you as well, where at first I tried to do a belongs to through, but it turns out that belongs to does not support the through option. And then, I kind of went down the next path of thinking about if I could do a has one, a device has one company through employee, right? But the more I thought about it, the kind of stranger it felt to me in terms of the semantics of saying that a device has a company as opposed to a company having a device. It made more sense in plain English to think about it in terms of a device belonging to a company. JOËL: That's interesting, right? Because those are ways of describing relationships in sort of ActiveRecord's language. And in sort of a richer situation, you might have all sorts of different adjectives to describe relationships. Instead of just belongs to has many, you have things like an employee owns a device, an employee works for a company, you know because an employee doesn't literally belong to a company in the literal sense. That's kind of messed up. So, I think what ActiveRecord's language is trying to use is less trying to, like, hit maybe, like, the English domain language of how these things relate to, and it's more about where the foreign keys are in the database. STEPHANIE: Yeah. I like that point where even though, you know, these are the things that are available to us, that doesn't actually necessarily, you know, capture what we want it to mean. And I had gone to see what Rails' recommendation was, not necessarily for the situation I shared. But they have a section for choosing between which model should have the belongs to, as opposed to, like, it has one association on it. And it says, like you mentioned, you know, the distinction is where you place the foreign key, but you should kind of think about the actual meaning of the data. And, you know, we've talked a lot about, I think, domain modeling [chuckles] on the show. But their kind of documentation says that...the has something relationship says that one of something is yours, that it can, like, point back to you. And in the example I shared, it still felt to me like, you know, really, the device wanted to point to the company that it is owned by. And if we think about it in real-world terms, too, if that device, like, is company property, for example, then that's a way that that does make sense. But the couple of paths forward that I saw in front of me were to rework that association, maybe add a new column onto the device, and go down that path of codifying it at the database level. Or kind of maybe something as, like, an in-between step is delegating the method to the employee. And that's what I ended up doing because I wasn't quite ready to do that data migration. JOËL: Adding more columns is interesting because then you can run into sort of data consistency issues. Let's say on the device you have a company ID to see who the device belongs to. Now, there are sort of two different independent paths. You can ask, "Which company does this device belong to?" You can either check the company ID and then look it up in the company table. Or you can join on the employee and join the employee back under company. And those might give you different answers and that can be a problem with data consistency if those two need to stay in sync. STEPHANIE: Yeah, that is a good point. JOËL: There could be scenarios where those two are allowed to diverge, right? You can imagine a scenario where maybe a company owns the device, but an employee of a potentially different company is using the device. And so, now it's okay to have sort of two different chains because the path through the employee is about what company is using our devices versus which company actually owns them. And those are, like, two different kinds of relationships. But if you're trying to get the same thing through two different paths of joining, then that can set you up for some data inconsistency issues. STEPHANIE: Wow. I really liked what you said there because I don't think enough thought goes into the emergent relationships between models after they've been introduced to a codebase. At least in my experience, I've seen a lot of thought go up front into how we might want to model an ActiveRecord, but then less thought into seeing what patterns kind of show up over time as we introduce more functionality to these models, and kind of understand how they should exist in our codebase. Is that something that you find yourself kind of noticing? Like, how do you kind of pick up on the cue that maybe there's some more thought that needs to happen when it comes to existing database tables? JOËL: I think it's something that definitely is a bit of a red flag, for me, is when there are multiple paths to connect to sort of establish a relationship. So, if I were to draw out some sort of, like, diagram of the models, boxes, and arrows or something like that, and then I could sort of overlay different paths through that diagram to connect two models and realize that those things need to be in sync, I think that's when I started thinking, ooh, that's a potential danger. STEPHANIE: Yeah, that's a really great point because, you know, the example I shared was actually a kind of contrived one based on what I was seeing in a client codebase, not, you know, I'm not actually working with devices, companies, and employees [laughs]. But it was encoded as, essentially, a device having one company. And I ended up drawing it out because I just couldn't wrap my head around that idea. And I had, essentially, an arrow from device pointing to company when I could also see that you could go take the path of going through employee [laughs]. And I was just curious if that was intentional or was it just kind of a convenient way to have that direct method available? I don't currently have enough context to determine but would be something I want to pay attention to. Like you said, it does feel like, if not a red flag, at least an orange one. JOËL: And there's a whole kind of science to some of this called database normalization, where they're sort of, like, they all have rather arcane names. They're the first normal form, the second normal form, the third normal form, you know, it goes on. If you look at the definition, they're all also a little bit arcane, like every element in a relation must depend solely upon the primary key. And you're just like, well, what does that mean? And how do I know if my table is compliant with that? So, I think it's worth, if you're Googling for some of these, find an article that sort of explains these a little bit more in layman's terms, if you will. But the general idea is that there are sort of stricter and stricter levels of the amount of sort of duplicate sources of truth you can have. In a sense, it's almost like DRY but for databases, and for your database schema in particular. Because when you have multiple sources of truth, like who does this device belong to, and now you get two different answers, or three different answers, now you've got a data corruption issue. Unlike bugs in code where it's, you know, it can be a problem because the site is down, or users have incorrect behavior, but then you can fix it later, and then go to production, and disruption to your clients is the worst that happened, this sort of problem in data is sometimes unrecoverable. Like, it's just, hey, -- STEPHANIE: Whoa, that sounds scary. JOËL: Yeah, no, data problems scare me in a way that code problems don't. STEPHANIE: Whoa. Could you...I think I interrupted you. But where were you going to go about once you have corrupted data? Like, it's unrecoverable. What happens then? JOËL: Because, like, if I look at the database, do I know who the real owner of this...if I want to fix it, let's say I fix my schema, but now I've got all this data where I've got devices that have two different owners, and I don't know which one is the real one. And maybe the answer is, I just sort of pick one and say, "Oh, the one that was through this association is sort of the canonical one, and we can just sort of ignore the other one." Do I have confidence in that decision? Well, maybe depending on some of the other context maybe, I'm lucky that I can have that. The doomsday scenario is that it's a little bit of one, a little bit of the other because there were different code paths that would write to one way or another. And there's no real way of knowing. If there's not too many devices, maybe I do an audit. Maybe I have to, like, follow up with all of my customers and say, "Hey, can you tell me which ones are really your devices?" That's not going to scale. Like, real worst case scenario, you almost have to do, like, a bit of a bankruptcy, where you say, "Hey, all the data prior to this date there's a bit of a question mark on it. We're not a hundred percent sure about it." And that does not feel great. So, now you're talking about mitigation strategies. STEPHANIE: Oof. Wow. Yeah, you did make it sound [laughs] very scary. I think I've kind of been on the periphery of a situation like this before, where it's not just that we couldn't trust the code. It's that we couldn't trust the data in the database either to tell us how things work, you know, for our users and should work from a product perspective. And I was on a previous client project where they had to, yeah, like, hire a bunch of people to go through that data and kind of make those determinations, like you said, to kind of figure out it out for, you know, all of these customers to determine the source of truth there. And it did not sound like an easy feat at all, right? That's so much time and investment that you have to put into that once you get to that point. JOËL: And there's a little bit of, like, different problems at different layers. You know, at the database layer, generally, you want all of that data to be really in a sort of single source of truth. Sometimes that makes it annoying to query because you've got to do all these joins. And so, there are various denormalization strategies that you can use to make that. Or sometimes it's a risk you're going to take. You're going to say, "Look, this table is not going to be totally normalized. There's going to be some amount of duplication, and we're comfortable with the risk if that comes up." Sometimes you also build layers of abstractions on top, so you might have your data sort of at rest in database tables fully normalized and separated out, but it's really clunky to query. So, you build out a database view on top of that that returns data in sort of denormalized fashion. But that's okay because you can always get your correct answer by querying the underlying tables. STEPHANIE: Wow. Okay. I have a lot of thoughts about this because I feel like database normalization, and I guess denormalization now, are skills that I am certainly not an expert at. And so, when it comes to, like, your average developer, how much do you think that people need to be thinking about this? Or what strategies do you have for, you know, a typical Rails dev in terms of how deep they should go [laughs]? JOËL: So, the classic advice is you probably want to go to, like, third to fourth normal form, usually three. There's also like 3.5 for some reason. That's also, I think, sometimes called BNF. Anyway, sort of levels of how much you normalize. Some of these things are, like, really, really basic things that Rails just builds into its defaults with that convention over configuration, so things like every table should have a primary key. And that primary key should be something that's fixed and unique. So, don't use something like combination of first name, last name as your primary key because there could be multiple people with the same name. Also, people change their names, and that's not great. But it's great that people can change their names. It's not great to rely on that as a primary key. There are things like look for repeating columns. If you've got columns in your schema with a number prefix at the end, that's probably a sign that you want to extract a table. So, I don't know, you have a movie, and you want to list the actors for a movie. If your movie table has actor 1, actor 2, actor 3, actor 4, actor 5, you know, like, all the way up to actor 20, and you're just like, "Yeah, no, we fill, like, actor 1 through N, and if there's any space left over, we just put nulls in those columns," that's a pretty big sign that, hey, why don't you instead have a, like, actor's table, and then make a, like, has many association? So, a lot of the, like, really basic normalization things, I think, are either built into Rails or built into sort of best practices around Rails. I think something that's really useful for developers to get as a sense beyond learning the actual different normal forms is think about it like DRY for your schema. Be wary of sort of multiple sources of truth for your data, and that will get you most of the way there. When you're designing sort of models and tables, oftentimes, we think of DRY more in terms of code. Do you ever think about that a little bit in terms of your tables as well? STEPHANIE: Yeah, I would say so. I think a lot of the time rather than references to another table just starting to grow on a certain model, I would usually lean towards introducing a join table there, both because it kind of encapsulates this idea that there is a connection, and it makes the space for that idea to grow if it needs to in the future. I don't know if I have really been disciplined in thinking about like, oh, you know, there should really...every time I kind of am designing my database tables, thinking about, like, there should only be one source of truth. But I think that's a really good rule of thumb to follow. And in fact, I can actually think of an example right now where we are a little bit tempted to break that rule. And you're making me reconsider [laughter] if there's another way of doing so. One thing that I have been kind of appreciative of lately is on my current client project; there's just, like, a lot of data. It's a very data-intensive and sensitive application. And so, when we introduce migrations, those PRs get tagged for review by someone over from the DevOps side, just to kind of provide some guidance around, you know, making sure that we're setting up our models to scale well. One of the things that he's been asking me on my couple of code changes I introduced was, like, when I introduced an index, like, it happened to be, like, a composite index with a couple of different columns, and the particular order of those columns mattered. And he kind of prompted me to, like, share what my use cases for this index were, just to make sure that, like, some thought went into it, right? Like, it's not so much that the way that I had done it was wrong, but just that I had, like, thought about it. And I like that as a way of kind of thinking about things at the abstraction that I need to to do my dev work day to day and then kind of mapping that to, like you were saying, those best practices around keeping things kind of performant at the database level. JOËL: I think there's a bit of a parallel world that people could really benefit from dipping a toe in, and that's sort of the typed programming world, this idea of making impossible states impossible or making illegal states unrepresentable. That in the sort of now it's not schemas of database tables or schemas of types that you're creating but trying to prevent data coming into a state where someone could plausibly construct an instance of your object or your type that would be nonsensical in the context of your app, kind of trying to lock that down. And I think a lot of the ways that people in those communities think about...in a sense, it's kind of like database normalization for developers. So, if you're not wanting to, like, dip your toe in more of the sort of database-centric world and, like, read an article from a DBA, it might be worthwhile to look at some of those worlds as well. And I think a great starting point for that is a talk by Richard Feldman called Making Impossible States Impossible. It's for the Elm language. And there are equivalents, I think, in many others as well. STEPHANIE: That's really cool that you are making that connection. I know we've kind of briefly talked about workshops in the past on the show. But if there were a workshop for, you know, that kind of database normalization for developers, I would be the first to sign up [laughs]. JOËL: Hint, hint, RailsConf idea. There's something from your original question that I think is interesting to circle back to, and that's the fact that it was awkward to work through in Ruby to do the work that you wanted to do because the tables were laid out in a certain way. And sometimes, there's certain ways that you need the tables to be in order to be sort of safe to represent data, but they're not the optimal way that we would like to interact with them at the Ruby level. And I think it's okay for not everything in Ruby to be 100% reflective of the structure of the tables underneath. ActiveRecord gives us a great pattern, but everything is kind of one-to-one. And it's okay to layer on some things on top, add some extra methods to build some, like, connections in Ruby that rely on this normalized data underneath but that make life easier for you, or they better just represent or describe the relationships that you have. STEPHANIE: 100%. I was really compelled by your idea of introducing helpers that use more descriptive adjectives for what that relationship is like. We've talked about how Rails abstracted things from the database level, you know, for our convenience, but that should not stop us from, like, leaning on that further, right? And kind of introducing our own abstractions for those connections that we see in our domain. So, I feel really inspired. I might even kind of reconsider the way I handled the original example and see what I can make of it. JOËL: And I think your original solution of doing the delegation is a great example of this as well. You want the idea that a device belongs to a company or has an association called company, and you just don't want to go through that long chain, or at least you don't want that to be visible as an implementation detail. So, in this case, you delegate it through a chain of methods in Ruby. It could also be that you have a much longer chain of tables, and maybe they don't all have associations in Rails and all that. And I think it would be totally fine as well to define a method on an object where, I don't know, a device, I don't know, has many...let's call it technicians, which is everybody who's ever touched this device or, you know, is on a log somewhere for having done maintenance. And maybe that list of technicians is not a thing you can just get through regular Rails associations. Maybe there's a whole, like, custom query underlying that, and that's okay. STEPHANIE: Yeah, as you were saying that, I was thinking about that's actually kind of, like, active models are the great spot to put those methods and that logic. And I think you've made a really good case for that. JOËL: On that note, shall we wrap up? STEPHANIE: Let's wrap up. Show notes for this episode can be found at bikeshed.fm. JOËL: This show has been produced and edited by Mandy Moore. STEPHANIE: If you enjoyed listening, one really easy way to support the show is to leave us a quick rating or even a review in iTunes. It really helps other folks find the show. JOËL: If you have any feedback for this or any of our other episodes, you can reach us @_bikeshed, or you can reach me @joelquen on Twitter. STEPHANIE: Or reach both of us at hosts@bikeshed.fm via email. JOËL: Thanks so much for listening to The Bike Shed, and we'll see you next week. ALL: Byeeeeeeee!!!!!!!!!! AD: Did you know thoughtbot has a referral program? If you introduce us to someone looking for a design or development partner, we will compensate you if they decide to work with us. More info on our website at: tbot.io/referral. Or you can email us at: referrals@thoughtbot.com with any questions.

    424: The Spectrum of Automated Processes for Your Dev Team

    Play Episode Listen Later Apr 30, 2024 36:47


    Joël shares his experience with the dry-rb suite of gems, focusing on how he's been using contracts to validate input data. Stephanie relates to Joël's insights with her preparation for RailsConf, discussing her methods for presenting code in slides and weighing the aesthetics and functionality of different tools like VS Code and Carbon.sh. She also encounters a CI test failure that prompts her to consider the implications of enforcing specific coding standards through CI processes. The conversation turns into a discussion on managing coding standards and tools effectively, ensuring that automated systems help rather than hinder development. Joël and Stephanie ponder the balance between enforcing strict coding standards through CI and allowing developers the flexibility to bypass specific rules when necessary, ensuring tools provide valuable feedback without becoming obstructions. Transcript: AD: We're excited to announce a new workshop series for helping you get that startup idea you have out of your head and into the world. It's called Vision to Value. Over a series of 90-minute working sessions, you'll work with a thoughtbot product strategist and a handful of other founders to start testing your idea in the market and make a plan for building an MVP. Join for all seven of the weekly sessions, or pick and choose the ones that address your biggest challenge right now. Learn more and sign up at tbot.io/visionvalue. STEPHANIE: Hello and welcome to another episode of the Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I'm Stephanie Minn. JOËL: And I'm Joël Quenneville. And together, we're here to share a bit of what we've learned along the way. STEPHANIE: So, Joël, what's new in your world? JOËL: I've been working on a project that uses the dry-rb suite of gems. And one of the things we're doing there is we're validating inputs using this concept of a contract. So, you sort of describe the shape and requirements of this, like hash of attributes that you get, and it will then tell you whether it's valid or not, along with error messages. We then want to use those to eventually build some other sort of value object type things that we use in the app. And because there's, like, failure points at multiple places that you have to track, it gets a little bit clunky. And I got to thinking a little bit about, like, forget about the internal machinery. What is it that I would actually like to happen here? And really, what I want is to say, I've got this, like, bunch of attributes, which may or may not be correct. I want to pass them into a method, and then either get back a value object that I was hoping to construct or some kind of error. STEPHANIE: That sounds reasonable to me. JOËL: And then, thinking about it just a little bit longer, I was like, wait a minute, this idea of, like, unstructured input goes into a method, you get back something more structured or an error, that's kind of the broad definition of parsing. I think what I'm looking for is a parser object. And this really fits well with a style of processing popularized in the functional programming community called parse, don't validate the idea that you use a parser like this to sort of transform data from more loose to more strict values, values where you can have more assumptions. And so, I create an object, and I can take a contract. I can take a class and say, "Attempt to take the following attributes. If they're valid according to the construct, create this classroom." And it, you know, does a bunch of error handling and some...under the hood, dry-rb does all this monad stuff. So, I handled that all inside of the object, but it's actually really nice. STEPHANIE: Cool. Yeah, I had a feeling that was where you were going to go. A while back, we had talked about really impactful articles that we had read over the course of the year, and you had shared one called Parse, Don't Validate. And that heuristic has actually been stuck in my head a little bit. And that was really cool that you found an opportunity to use it in, you know, previously trying to make something work that, like, you weren't really sure kind of how you wanted to implement that. JOËL: I think I had a bit of a light bulb moment as I was trying to figure this out because, in my mind, there are sort of two broad approaches. There's the parse, don't validate where you have some inputs, and then you transform them into something stricter. Or there's more of that validation approach where you have inputs, you verify that they're correct, and then you pass them on to someone else. And you just say, "Trust me, I verified they're in the right shape." Dry-rb sort of contracts feel like they fit more under that validation approach rather than the parse, don't validate. Where I think the kind of the light bulb turned on for me is the idea that if you pair a validation step and an object construction step, you've effectively approximated the idea of parse, don't validate. So, if I create a parser object that says, in sort of one step, I'm going to validate some inputs and then immediately use them if they're valid to construct an object, then I've kind of done a parse don't validate, even though the individual building blocks don't follow that pattern. STEPHANIE: More like a parse and validate, if you will [laughs]. I have a question for you. Like, do you own those inputs kind of in your domain? JOËL: In this particular case, sort of. They're coming from a form, so yes. But it's user input, so never trust that. STEPHANIE: Gotcha. JOËL: I think you can take this idea and go a little bit broader as well. It doesn't have to be, like, the dry-rb-related stuff. You could do, for example, a JSON schema, right? You're dealing with the input from a third-party API, and you say, "Okay, well, I'm going to have a sort of validation JSON schema." It will just tell you, "Is this data valid or not?" and give you some errors. But what if you paired that with construction and you could create a little parser object, if you wanted to, that says, "Hey, I've got a payload coming in from a third-party API, validate it against this JSON schema, and attempt to construct this shopping cart object, and give me an error otherwise." And now you've sort of created a nice, little parse, don't validate pipeline which I find a really nice way to deal with data like that. STEPHANIE: From a user perspective, I'm curious: Does this also improve the user experience? I'm kind of wondering about that. It seems like it could. But have you explored that? JOËL: This is more about the developer experience. STEPHANIE: Got it. JOËL: The user experience, I think, would be either identical or, you know, you can play around with things to display better errors. But this is more about the ergonomics on the development side of things. It was a little bit clunky to sort of assemble all the parts together. And sometimes we didn't immediately do both steps together at the same time. So, you might sort of have parameters that we're like, oh, these are totally good, we promise. And we pass them on to someone else, who passes them on to someone else. And then, they might try to do something with them and hope that they've got the data in the right shape. And so, saying, let's co-locate these two things. Let's say the validation of the inputs and then the creation of some richer object happen immediately one after another. We're always going to bundle them together. And then, in this particular case, because we're using dry-rb, there's all this monad stuff that has to happen. That was a little bit clunky. We've sort of hidden that in one object, and then nobody else ever has to deal with that. So, it's easier for developers in terms of just, if you want to turn inputs into objects, now you're just passing them into one object, into one, like, parser, and it works. But it's a nicer developer experience, but also there's a little bit more safety in that because now you're sort of always working with these richer objects that have been validated. STEPHANIE: Yeah, that makes sense. It sounds very cohesive because you've determined that these are two things that should always happen together. The problems arise when they start to actually get separated, and you don't have what you need in terms of using your interfaces. And that's very nice that you were able to bundle that in an abstraction that makes sense. JOËL: A really interesting thing I think about abstractions is sometimes thinking of them as the combination of multiple other things. So, you could say that the combination of one thing and another thing, and all of a sudden, you have a new sort of combo thing that you have created. And, in this case, I think the combination of input validation and construction, and, you know, to a certain extent, error handling, so maybe it's a combination of three things gives you a thing you can call a parser. And knowing that that combination is a thing you can put a name on, I think, is really powerful, or at least it felt really powerful to me when that light bulb turned on. STEPHANIE: Yeah, it's kind of like the whole is greater than the sum of its parts. JOËL: Yeah. STEPHANIE: Cool. JOËL: And you and I did an episode on Specialized Vocabulary a while back. And that power of naming, saying that, oh, I don't just have a bunch of little atomic steps that do things. But the fact that the combination of three or four of them is a thing in and of itself that has a name that we can talk about has properties that we're familiar with, all of a sudden, that is a really powerful way to think about a system. STEPHANIE: Absolutely. That's very exciting. JOËL: So, Stephanie, what's new in your world? STEPHANIE: So, I am plugging away at my RailsConf talk, and I reached the point where I'm starting to work on slides. And this talk will be the first one where I have a lot of code that I want to present on my slides. And so, I've been playing around with a couple of different tools to present code on slides or, I guess, you know, just being able to share code outside of an editor. And the two tools I'm trying are...VS Code actually has a copy with syntax functionality in its command palette. And so, that's cool because it basically, you know, just takes your editor styling and applies it wherever you paste that code snippet. JOËL: Is that a screenshot or that's, like, formatted text that you can paste in, like, a rich text editor? STEPHANIE: Yeah, it's the latter. JOËL: Okay. STEPHANIE: That was nice because if I needed to make changes in my slides once I had already put them there, I could do that. But then the other tool that I was giving a whirl is Carbon.sh. And that one, I think, is pretty popular because it looks very slick. It kind of looks like a little Mac window and is very minimal. But you can paste your code into their text editor, and then you can export PNGs of the code. So, those are just screenshots rather than editable text. And I [chuckles] was using that, exported a bunch of screenshots of all of my code in various stages, and then realized I had a typo [laughs]. JOËL: Oh no! STEPHANIE: Yeah, so I have not got around to fixing that yet. That was pretty frustrating because now I would have to go back and regenerate all of those exports. So, that's kind of where I'm at in terms of exploring sharing code. So, if anyone has any other tools that they would use and recommend, I am all ears. JOËL: How do you feel about balancing sort of the quantity of code that you put on a slide? Do you tend to go with, like, a larger code slide and then maybe, like, highlight certain sections? Do you try to explain ideas in general and then only show, like, a couple of lines? Do you show, like, maybe a class that's got ten lines, and that's fine? Where do you find that balance in terms of how much code to put on a slide? Because I feel like that's always the big dilemma for me. STEPHANIE: Yeah. Since this is my first time doing it, like, I really have no idea how it's going to turn out. But what I've been trying is focusing more on changes between each slide, so the progression of the code. And then, I can, hopefully, focus more on what has changed since the last snippet of code we were looking at. That has also required me to be more fiddly with the formatting because I don't want essentially, like, the window that's containing the code to be changing sizes [laughs] in between slide transitions. So, that was a little bit finicky. And then, there's also a few other parts where I am highlighting with, like, a border or something around certain texts that I will probably pause and talk about, but yeah, it's tough. I feel like I've seen it done well, but it's a lot harder to and a lot more effort to [laughs] do in practice, I'm finding. JOËL: When someone does it well, it looks effortless. And then, when somebody does it poorly, you're like, okay, I'm struggling to connect with this talk. STEPHANIE: Yep. Yep. I hear that. I don't know if you would agree with this, but I get the sense that people who are able to make that look effortless have, like, a really deep and thorough understanding of the code they're showing and what exactly they think is important for the audience to pay attention to and understand in that given moment in their talk. That's the part that I'm finding a lot more work [laughs] because just thinking about, you know, the code I'm showing from a different lens or perspective. JOËL: How do you sort of shrink it down to only what's essential for the point that you're trying to make? And then, more broadly, not just the point you're trying to make on this one slide, but how does this one slide fit into the broader narrative of the story you're trying to tell? STEPHANIE: Right. So, we'll see how it goes for me. I'm sure it's one of those things that takes practice and experience, and this will be my first time, and we'll learn something from it. JOËL: That's exciting. So, this is RailsConf in Detroit this year, I believe, May 7th through 9th. STEPHANIE: Yep. That's right. So, recently on my client work, I encountered a CI failure on a PR of mine that I was surprised by. And basically, I had introduced a new association on a model, and this CI failure was saying like, "Hey, like, we see that you introduced this association. You should consider adding this to the presenter for this model." And I hadn't even known that that presenter existed [laughs]. So, it was kind of interesting to get a CI failure nudging me to consider if I need to be, like, making a different, you know, this other change somewhere else. JOËL: That's a really fun use of CI. Do you think that was sort of helpful for you as a newer person on that codebase? Or was it more kind of annoying and, like, okay, this CI is over the top? STEPHANIE: You know, I'm not sure [laughs]. For what it's worth, this presenter was actually for their admin dashboard, essentially. And so, the goal of what this workflow was trying to do was help folks who are using the admin dashboard have, like, all of the capabilities they need to do that job. And it makes sense that as you add behavior to your app, sometimes those things could get missed in terms of supporting, you know, not just your customers but developers, support product, you know, the other users of your app. So, it was cool. And that was, you know, something that they cared enough to enforce. But yeah, I think there maybe is a bit of a slippery slope or at least some kind of line, or it might even be pretty blurry around what should our test failures really be doing. JOËL: And CI is interesting because it can be a lot more than just tests. You can run all sorts of things. You can run a linter that fails. You could run various code quality tools that are not things like unit tests. And I think those are all valid uses of the CI process. What's interesting here is that it sounds like there were two systems that needed to stay in sync. And this particular CI check was about making sure that we didn't accidentally introduce code that would sort of drift apart in those two places. Does that sound about right? STEPHANIE: Yeah, that does sound right. I think where it gets a little fuzzy, for me, is whether that kind of check was for code quality, was for a standard, or for a policy, right? It was kind of saying like, hey, like, this is the way that we've enforced developers to keep those two things from drifting. Whereas I think that could be also handled in different ways, right? JOËL: Yeah. I guess in terms of, like, keeping two things in sync, I like to do that at almost, like, a code level, if possible. I mean, maybe you need a single source of truth, and then it just sort of happens automatically. Otherwise, maybe doing it in a way that will yell at you. So, you know, maybe there's a base class somewhere that will raise an error, and that will get caught by CI, or, you know, when you're manually testing and like, oh yeah, I need to keep this thing in sync. Maybe you can derive some things or get fancy with metaprogramming. And the goal here is you don't have a situation where someone adds a new file in one place and then they accidentally break an admin dashboard because they weren't aware that you needed these two files to be one-to-one. If I can't do it just at a code level, I have done that before at, like, a unit test level, where maybe there's, like, a constant somewhere, and I just want to assert that every item in this constant array has a matching entry somewhere else or something like that, so that you don't end up effectively crashing the site for someone else because that is broken behavior. STEPHANIE: Yeah, in this particular case, it wasn't necessarily broken. It was asking you "Hey, should this be added to the admin presenter?" which I thought was interesting. But I also hear what you're saying. It actually does remind me of what we were talking about earlier when you've identified two things that should happen, like mostly together and whether the code gives you affordances to do that. JOËL: So, one of the things you said is really interesting, the idea that adding to the presenter might have been optional. Does that mean that CI failed for you but that you could merge anyway, or how does that work? STEPHANIE: Right. I should have been more clear. This was actually a test failure, you know, that happened to be caught by CI because I don't run [laughs] the whole test suite locally. JOËL: But it's an optional test failure, so you're allowed to let that test fail. STEPHANIE: Basically, it told me, like, if I want this to be shown in the presenter, add it to this method, or if not, add it to...it was kind of like an allow list basically. JOËL: I see. STEPHANIE: Or an ignore list, yeah. JOËL: I think that kind of makes sense because now you have sort of, like, a required consistency thing. So, you say, "Our system requires you...whenever you add a file in this directory, you must add it to either an allow list or an ignore list, which we have set up in this other file." And, you know, sometimes you might forget, or sometimes you're new, and it's your first time adding a file in this directory, and you didn't remember there's a different place where you have to effectively register it. That seems like a reasonable check to have in place if you're relying on these sort of allow lists for other parts of the system, and you need to keep them in sync. STEPHANIE: So, I think this is one of the few instances where I might disagree with you, Joël. What I'm thinking is that it feels a bit weird to me to enforce a decision that was so far away from the code change that I made. You know, you're right. On one hand, I am newer to this codebase, maybe have less of that context of different features, things that need to happen. It's a big app. But I almost think this test reinforces this weird coupling of things that are very far away from each other [laughs]. JOËL: So, it's maybe not the test itself you object to rather than the general architecture where these admin presenters are relying on these other objects. And by you introducing a file in a totally different part of the app, there's a chance that you might break the admin, and that feels weird to you. STEPHANIE: Yeah, that does feel weird to me. And then, also that this implementation is, like, codified in this test, I guess, as opposed to a different kind of, like, acceptance test, rather than specifying specifically like, oh, I noticed, you know, you didn't add this new association or attribute to either the allow list or the ignore list. Maybe there is a more, like, higher level test that could steer us in keeping the features consistent without necessarily dictating, like, that it needs to happen in these particular methods. JOËL: So, you're talking something like doing an integration test rather than a unit test? Or are you talking about something entirely different? STEPHANIE: I think it could be an integration test or a system test. I'm not sure exactly. But I am wondering what options, you know, are out there for helping keeping standards in place without necessarily, like, prescribing too much about, like, how it needs to be done. JOËL: So, you used the word standard here, which I tend to think about more in terms of, like, code style, things like that. What you're describing here feels a little bit less like a standard and more of what I would call a code invariant. STEPHANIE: Ooh. JOËL: It's sort of like in this architecture the way we've set up, there must always be sort of one-to-one matching between files in this directory and entries in this array. Now, that's annoying because they're sort of, like, two different places, and they can vary independently. So, locking those two in sync requires you to do some clunky things, but that's sort of the way the architecture has been designed. These two things must remain one-to-one. This is an invariant we want in the app. STEPHANIE: Can you define invariant for me [laughs], the way that you're using it here? JOËL: Yeah, so something that is required to be true of all elements in this class of things, sort of a rule or a law that you're applying to the way that these particular bits of code need to behave. So, in this case, the invariant is every file in this directory must have a matching entry in this array. There's a lot of ways to enforce that. The sort of traditional idea is sort of pushing a lot of that checking...they'll sometimes talk about pushing errors to the left. So, if you can handle this earlier in the sort of code execution pipeline, can you do it maybe with a type system if you're in a type language? Can you do it with some sort of input validation at runtime? Some languages have the concept of contracts, so maybe you enforce invariants using that. You could even do something really ad hoc in Ruby, where you might say, "Hey, at boot time, when we load this particular array for the admin, just load this directory. Make sure that the entries in the array match the entries in the directory, and if they don't, raise an error." And I guess you would catch that probably in CI just because you tried to run your test suite, and you'd immediately get this boot error because the entries don't match. So, I guess it kind of gets [inaudible 22:36] CI, but now it's not really a dedicated test anymore. It's more of, like, a property of the system. And so, in this case, I've sort of shifted the error checking or the checking of this invariant more into the architecture itself rather than in, like, things that exercise the architecture. But you can go the other way and say, "Well, let's shift it out of the architecture into tests," or maybe even beyond that, into, like, manual QA or, you know, other things that you can do to verify it. STEPHANIE: Hmm. That is very compelling to me. JOËL: So, we've been talking so far about the idea of invariants, but the thing about invariants is that they don't vary. They're always true. This is a sort of fundamental rule of how this system works. The class of problems that I often struggle with how to deal with in these sorts of situations are rules that you only sometimes want to apply. They're not consistent. Have you ever run into things like that? STEPHANIE: Yeah, I have. And I think that's what was compelling to me about what you were sharing about code invariance because I wasn't totally convinced this particular situation was a very clear and absolute rule that had been decided, you know, it seemed a little bit more ambiguous. When you're talking about, like, applying rules that sometimes you actually don't want to apply, I think of things like linters, where we want to disable, you know, certain rules because we just can't get around implementing the way we want to while following those standards. Or maybe, you know, sometimes you just have to do something that is not accessible [laughs], not that that's what I would recommend, but in the case where there aren't other levers to change, you maybe want to disable some kind of accessibility check. JOËL: That's always interesting, right? Because sometimes, you might want, like, the idea of something that has an escape hatch in it, but that immediately adds a lot of complexity to things as well. This is getting into more controversial territory. But I read a really compelling article by Jeroen Engels about how being able to, like, locally disable your linter for particular methods actually makes your code, but also the linter itself, a worse tool. And it really kind of made me rethink a little bit of how I approach linters as a tool. STEPHANIE: Ooh. JOËL: And what makes sense in a linter. STEPHANIE: What was the argument for the linter being a worse tool by doing that? JOËL: You know, it's funny that you ask because now I can't remember, and it's been a little while since I've read the article. STEPHANIE: I'll have to revisit it after the show [laughs]. JOËL: Apparently, I didn't do the homework for this episode, but we'll definitely link to that article in the show notes. STEPHANIE: So, how do you approach either introducing a new rule to something like a linter or maybe reconsidering an existing rule? Like, how would you go about finding, like, consensus on that from your team? JOËL: That varies a lot by organizational culture, right? Some places will do it top-down, some of them will have a broader conversation and come to a consensus. And sometimes you just straight up don't get a choice. You're pulling in a tool like standard rb, and you're saying, "Look, we don't want to have a discussion about every little style thing, so whatever, you know, the community has agreed on for the standard rb linter is the style we're using. There are no discussions. Do what the linter tells you." STEPHANIE: Yeah, that's true. I think I have to adapt to whatever, you know, client culture is like when I join new projects. You know, sometimes I do see people being like, "Hey, I think it's kind of weird that we have this," or, "Hey, I've noticed, for example, oh, we're merging focused RSpec tests. Like, let's introduce a rule to make sure that that doesn't happen." I also think that a different approach is for those things not to be enforced at all by automation, but we, you know, there are still guidelines. I think the thoughtbot guides are an example of pretty opinionated guidelines around style and syntax. But I don't think that those kinds of things would, you know, ever be, like, enforced in a way that would be blocking. JOËL: Those are kind of hard because they're not as consistent as you would think, so it's not a rule you can apply every time. It's more of a, here's some things to maybe keep in mind. Or if you're writing code in this way, think about some of the edge cases that might happen, or don't default to writing it in this way because things might go wrong. Make sure you know what you're doing. I love the phrase, "Must be able to justify this," or sometimes, "Must convince your pair that this is okay." So, default to writing in style A, avoid style B unless you can have a compelling reason to do so and can articulate that on your PR or, you know, convince your pair that that's the right way to go. STEPHANIE: Interesting. It's kind of like the honor system, then [laughs]. JOËL: And I think that's sort of the general way when you're working with developers, right? There's a lot of areas where there is ambiguity. There is no single best way to do it. And so, you rely on people's expertise to build systems that work well. There are some things where you say, look, having conversations about these things is not useful. We want to have some amount of standardization or uniformity about certain things. Maybe there's invariance you want to hold. Maybe there's certain things we're, like, this should never get to production. Whenever you've got these, like, broad sweeping statements about things should be always true or never true, that's a great time to introduce something like a linting rule. When it's more up to personal judgment, and you just want to nudge that judgment one way or another, then maybe it's better to have something like a guide. STEPHANIE: Yeah, what I'm hearing is there is a bit of a spectrum. JOËL: For sure. From things that are always true to things that are, like, sometimes true. I think I'm sort of curious about the idea of going a level beyond that, though, beyond things like just code style or maybe even, like, invariance you want to hold or something, being able to make suggestions to developers based off the code that is written. So, now you're applying more like heuristics, but instead of asking a human to apply those heuristics at code review time and leave some comments, maybe there's a way to get automated feedback from a tool. STEPHANIE: Yeah, I think we had mentioned code analysis tools earlier because some teams and organizations include those as part of their CI builds, right? And, you know, even Brakeman, right? Like, that's an analysis tool for security. But I can't recall if I've seen an organization use things like Flog metrics which measure code complexity in things like that. How would you feel if that were a check that was blocking your work? JOËL: So, I've seen things like that be used if you're using, like, the Code Climate plugin for GitHub. And Code Climate internally does effectively flog and other things that are fancier on your code quality. And so, you can set a threshold to say, hey, if complexity gets higher than a certain amount, fail the build. You can also...if you're doing things via GitHub, what's nice is that you can do effectively non-blocking comments. So, instead of failing CI to say, "Hey, this method looks really complex. You cannot merge until you have made this method less complex," maybe the sort of, like, next step up in ambiguity is to just leave a comment on a PR from a tool and say, "Hey, this method here is looking really complex. Consider breaking it up." STEPHANIE: Yeah, there is a tool that I've seen but not used called Danger, and its tagline is, Stop saying, "You forgot to..." in code review [laughs]. And it basically does that, what you were saying, of, like, leaving probably a suggestion. I can imagine it's blocking, but a suggestive comment that just automates that rather than it being a manual process that humans have to remember or notice. JOËL: And there's a lot of things that could be specific to your organization or your architecture. So, you say, "Hey, you introduced a file here. Would you consider also making an entry to this presenter file so that it's editable on the admin?" And maybe that's a better place to handle that. Just a comment. But you wouldn't necessarily want every code reviewer to have to think about that. STEPHANIE: So, I do think that I am sometimes not necessarily suspicious, but I have also seen tools like that end up just getting in the way, and it just becomes something you ignore. It's something you end up always using the escape hatch for, or people just find ways around it because they're harming more than they're helping. Do you have any thoughts about how to kind of keep those things in check and make sure that the tools we introduce genuinely are kind of helping the organization do the right thing rather than kind of being these perhaps arbitrary blockers? JOËL: I'm going to throw a fancy phrase at you. STEPHANIE: Ooh, I'm ready. JOËL: Signal-to-noise ratio. STEPHANIE: Whoa, uh-huh. JOËL: So, how often is the feedback from your tool actually helpful, and how often is it just noise that you have to dismiss, or manually override, or things like that? At some point, the ratio becomes so much that you lose the signal in all the noise. And so, maybe you even, like, because you're always just ignoring the feedback from this tool, you accidentally start overriding things that would be genuinely helpful. And, at that point, you've got the worst of both worlds. So, sort of keeping track on what that ratio is, and there's not, like, a magic number. I'm not going to tell you, "Oh, this is an 80/20 principle. You need to have, you know, 80% of the time it's useful and only 20% of the time it's not useful." I don't have a number to give you, but keeping track on maybe, you know, is it more often than not useful? Is your team getting to the point where they're just ignoring feedback from this tool? And thinking in terms of that signal versus that noise, I think is useful—to go back to that word again, heuristic for managing whether a tool is still helpful. STEPHANIE: Yeah. And I would even go on to say that, you know, I always appreciate when people in leadership roles keep an eye on these things. And they're like, "Oh, I've been hearing that people are just totally numb to this tool [laughs]" or, you know, "There's no engagement on this. People are just ignoring those signals." Any developer impacted by this, it is valid to bring it up if you're getting frustrated by it or just finding yourself, you know, having all of these obstacles getting in the way of your development process. JOËL: Sometimes, this can be a symptom that you're mixing too many classes of problems together in one tool. So, maybe there are things that are, like, really dangerous to your product to go live with them. Maybe it's, you know, something like Brakeman where you're doing security checks, and you really, ideally, would not go to production with a failing security check. And then, you've got some random other style things in there, and you're just like, oh yeah, whatever, it's this tool because it's mostly style things but occasionally gives you a security problem. And because you ignore it all the time, now you accidentally go to production with a security problem. So, splitting that out and say, "Look, we've got blocking and unblocking because we recognize these two classes of problems can be a helpful solution to this problem." STEPHANIE: Joël, did you just apply an object-oriented design principle to an organizational system? [laughter] JOËL: I may be too much of a developer. STEPHANIE: Cool. Well, I really appreciate your input on this because, you know, I was just kind of mulling over, like, how I felt about these kinds of things that I encounter as a developer. And I am glad that we got to kind of talk about it. And I think it gives me a more expanded vocabulary to, you know, analyze or reflect when I encounter these things on different client organizations. JOËL: And every organization is different, right? Like, you've got to learn the culture, learn the different elements of that software. What are the things that are invariant? What are the things that are dangerous that we don't want to ship without? What are the things that we're doing just for consistency? What are things which are, like, these are culturally things that we'd like to do? There's all these levels, and it's a lot to pick up. STEPHANIE: Yeah. At the end of the day, I think what I really liked about the last thing you said was being able to identify the problem, like the class of problem, and applying the right tool for the right job. It helps me take a step back and perhaps even think of different solutions that we might not have thought about earlier because we had just gotten so used to the one way of enforcing or checking things like that. JOËL: On that note, shall we wrap up? STEPHANIE: Let's wrap up. Show notes for this episode can be found at bikeshed.fm. JOËL: This show has been produced and edited by Mandy Moore. STEPHANIE: If you enjoyed listening, one really easy way to support the show is to leave us a quick rating or even a review in iTunes. It really helps other folks find the show. JOËL: If you have any feedback for this or any of our other episodes, you can reach us @_bikeshed, or you can reach me @joelquen on Twitter. STEPHANIE: Or reach both of us at hosts@bikeshed.fm via email. JOËL: Thanks so much for listening to The Bike Shed, and we'll see you next week. ALL: Byeeeeeee!!!!!! AD: Did you know thoughtbot has a referral program? If you introduce us to someone looking for a design or development partner, we will compensate you if they decide to work with us. More info on our website at: tbot.io/referral. Or you can email us at: referrals@thoughtbot.com with any questions.

    423: Cognitive Strategies for Coders

    Play Episode Listen Later Apr 16, 2024 39:52


    Stephanie is back with a book recommendation: "Thinking in Systems" by Donella Meadows. This book has helped to bolster her understanding of complex systems in environmental, organizational, and software contexts, particularly through user interactions and system changes. Joël describes his transformative experience watching last week's total solar eclipse. Together, they explore how systems thinking influences software development and team dynamics by delving into practical applications in writing and reading code, suggesting that understanding complex systems can aid developers in navigating and optimizing codebases and team interactions. Transcript:  JOËL: Hello and welcome to another episode of The Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I'm Joël Quenneville. STEPHANIE: And I'm Stephanie Minn, and together, we're here to share a bit of what we've learned along the way. JOËL: So, Stephanie, what's new in your world? STEPHANIE: I have a book recommendation today [laughs]. JOËL: Oh, I love book recommendations. STEPHANIE: It's been a little while, so I wanted to share what I've been reading that I think might be interesting to this audience. I'm reading Thinking in Systems by Donella Meadows. Joël, are you familiar with systems thinking theory at all? JOËL: Very superficially. Hearing people talk about it on, I guess, X, now Twitter. STEPHANIE: Yeah. Well, what I like about this book is the subtitle is A Primer on Thinking in Systems [chuckles], which is perfect for me as someone who also just kind of understood it very loosely, as just like, oh, like, I dunno, you look at things holistically and look at the stuff, not just its parts but from a higher perspective. JOËL: Yeah. Is that accurate sort of your pre-book reading overview? Or do you think there's a bigger thing, a bigger idea there that the book unpacks? STEPHANIE: Yeah. I think I'm only, like, a third of the way through so far. But what I have enjoyed about it is that, you know, in some ways, like, intuitively, that makes a lot of sense about, like, oh yeah, you want to make sure that you see the forest for the trees, right? But one thing I've been surprised by is how it's also teaching me more technical language to talk about complex systems. And, in this case, she is talking about, essentially, living systems or systems that change over time where things are happening. I think that can be a little bit confusing when we also are, you know, talking about computer systems, but, in this case, you know, systems like environments, or communities, or even, you know, companies or organizations, which is actually where I'm finding a lot of the content really valuable. But some of the language that I've learned that I am now trying to integrate a little bit more into how I view a lot of just, like, daily problems or experiences involve things like feedback loops that might be reinforcing or balancing and different, like, inputs and output flows and what is driving those things. So, I've appreciated just having more precise language for things that I think I kind of intuited but didn't exactly know how to, like, wrap up in a way to communicate to someone. JOËL: Do you think the idea of thinking in terms of things like self-balancing versus sort of diverging input loops is something that's useful when actually writing code? Or do you think of it a little bit more in terms of, like, teams and how they organize general problem-solving approaches, things like that? STEPHANIE: I think the answer is both. I actually gave this quite a bit of thought because I was trying to wrap my head around her definition of a system and how we talk about systems sometimes, like, a codebase, for example. And the conclusion I came to is that, really, it's not just the code static by itself that we care about. It's how it gets exercised, how users use it, how developers change it, how we interact with it when we, like, run tests, for example. So, that was really helpful in kind of thinking about some of the problems we see in engineering organizations as a result of software being a thing that is used and written by humans, as opposed to it just existing in memories [chuckles] or, like, it's in a storage system somewhere. Like, that means it's kind of lifeless, and it's not changing anymore. But the point of kind of this framework is trying to understand it as it changes. JOËL: So, kind of that blurry line between humans and computers and where those two overlap is where a lot of that systems thinking almost, like, mental model or vocabulary has been most helpful for you. STEPHANIE: Yeah, I would say so. So, Joël, what's new in your world? JOËL: So, I did the thing. I traveled to see the total solar eclipse this past weekend. It was mind-blowing. It was incredibly cool. I really loved it. For any of our listeners who have never seen a solar eclipse, in the coming years, have an opportunity to see one. I'd say it's worth traveling to see because it is really impressive. STEPHANIE: Cool. What did it look like when it happened, when it was 100% eclipsed? JOËL: So, what really impressed me was the fact that, like, most of the cool stuff happens in that, like, last half a percent. So, like, 95% eclipsed, still not that impressive. If that's all I'd seen, I would be disappointed. And then, in that last little bit, all of a sudden, everything goes dark. It's sort of, like, that twilight past sunset. You've got a glow on the horizon. The stars are out. STEPHANIE: Wow. JOËL: The animals are behaving like it's past sunset. They're getting ready to go to sleep. STEPHANIE: Whoa. JOËL: The sun itself is just a black dot with this, like, big fiery ring around it. Like all those pictures, icons, photos you see online, or drawings that look over the top, those things are real. That's what it looks like. STEPHANIE: Wow, that's really neat. Could you see it without looking through the eclipse viewers? JOËL: So, when you hit totality, you can look at it with a naked eye, and it is, yeah, magnificent. STEPHANIE: Oh, that's so cool. How long did it last? JOËL: So, it depends where you are in the path of totality. I was pretty much dead center. And it lasts, I think, three and a half minutes is what we had. STEPHANIE: That's so cool. So, for me, here in Chicago, we did not have complete totality. It was about, like, 95%. So, I was watching it, just from that perspective. And I would say, yeah, it was not nearly as cool as what you described. It kind of just was like, oh, it got dark. It almost looked like I was viewing the world through sunglasses. I did have one of those viewers that I used to, like, look at the sun and see how much of it had been covered. But yeah, it was cool. But what you said, I think now I feel like, wow, I really should have [laughter] traveled. I could have traveled just a few hours, you know, to, like, Indianapolis or something to have been on the path. That would have been really neat. And I don't think the next one will be until 2044 or something like that. JOËL: Yeah. And that's the thing, right? I think if you're within a few hours of the path of a total eclipse, it is absolutely worth traveling to totality. The downside of that is that everybody else has the same idea. And so, you will be fighting traffic and a lot of things, especially if it goes through some, like, populated areas, like it did this time. STEPHANIE: Yeah. Well, that's really neat that you got to see that. That's, I don't know, it sounds like not exactly once in a lifetime, but definitely very rare. JOËL: For sure. I think with this experience now; I would definitely consider traveling again if there's one, like, anywhere near where I live, or, you know, maybe even, like, planning a vacation around going somewhere else to see one because it's short. You know, you're there for three minutes, and you see something cool. But that was really impressive. So, something that really struck me when you were talking earlier about systems thinking is that you mentioned that it gave you a sort of a new vocabulary to talk about things. It almost gave you a sort of different way of thinking or some other mental models that you could use to apply when you are interacting in that sort of fussy boundary between people and code. And I think that this idea of having language and having mental models is something that is incredibly valuable for us as programmers in a few different areas. And I'd be curious to see particularly for when we're reading other code, reading code that someone else has written or, you know, yourself from six months ago, do you have any sort of mental models that you like to reach for or techniques that you like to use to sort of give yourself that almost vocabulary to understand what somebody else is trying to do with their code? STEPHANIE: Yeah, I would say so. You know, as you were talking about, like, how do you read code? I was thinking about how I read code is different from how I would read a book [laughs]. I almost rarely just read everything line by line and, like, file by file, you know, in some order that has been presented to me. I am usually a lot more involved. It's almost, like, more like a choose your own adventure kind of book [chuckles], where it's like, oh, go to this page to check if you want to check out what happened down this code path [chuckles]. JOËL: Right, right. Oh, if you're reading a novel, are you the kind of person that will read the ending first? STEPHANIE: Absolutely not. [laughter] JOËL: You have strong opinions here. STEPHANIE: Even when I, like, really want to... okay, sometimes I will, like, maybe just kind of flip to the back and just see, like, oh, how many more pages or chapters do I have [laughs] left? If I am itching to know what might happen. But I definitely don't start a book by reading the end. I think there are people who do that, and maybe that works for them, but I don't understand it. [laughter] JOËL: But maybe that's the thing that you do with your code. STEPHANIE: Yeah. When I read code, it's almost always with some kind of intention to understand a particular behavior, usually kind of kicked off by some action, like, done by the user or something automated. And I want to understand that process from start to finish. So, I'm less likely to read a whole class file [chuckles], as opposed to just following method and the messages that are sent along the way in a process. JOËL: That makes sense. Do you tend to sort of go from kind of the origin point and then follow it down, or sort of the opposite, find some, like, terminal node and then work your way back? STEPHANIE: Oh. JOËL: And I could imagine this in a more concrete sense in a Rails app. You find, like, the route that you're going to hit because you know it's a URL, and then you find the controller, and then you read through the action. And then, you maybe follow a service and something like that or look into the view. Or maybe the opposite: there's a particular page that gets rendered. You look at a method, a helper method that gets called in a view, and then you sort of, like, follow a backtrace from there. STEPHANIE: Yeah, I think both. It depends on what information I have available to me, I think. I can think of, recently, I was trying to figure out the process for which, like, a user in this application I'm working on can downgrade the tier of their account, and I didn't know what to grep for. And so, I asked, like, "Hey, like, what are the entry points for a user being able to do this?" And someone gave me a couple of routes, and that was great because then I got to see, oh, that this is possible in multiple ways. Like, the user can do it themselves, or the admin can do it, and that was really helpful. Other times, I think I have been able to find a keyword on a page and start from, like, a view or a component, or something like that, and then work upwards. JOËL: I love that question that you asked, "What are the entry points for this thing?" I feel like that's a fantastic question to sort of ask yourself when you're feeling stuck, but it's also a great question to ask other people that might know. Do you find that you read code differently when you're just trying to, like, maybe understand a broader subsystem? Maybe you're sort of new to this area and you have to add a feature, as opposed to maybe you're debugging something and trying to understand why things went wrong. Are those two different kinds of reading? STEPHANIE: Yeah, that's also a great point because I do think there's another time when I've just scanned the file structure of an app and looked at the model's directory and just kind of been like, okay, like, maybe some things are namespaced. And that helps me just know what the main concepts that I have to be dealing with or that I will be dealing with are. But I find that sometimes less fruitful because of kind of what I mentioned earlier about thinking in systems, where I'm not sure how important those things will be yet because I don't know how they're used. They could not be used at all [laughs]. And then, I think I'm potentially, like, storing information that is not actually relevant in my brain. JOËL: That's tough, right? Because systems are so big, we can't hold them entirely in our brain. So, sometimes, selectively deciding what will not be loaded in there is just as important as what will. STEPHANIE: Yes. And I think that is actually advice that I would give to devs who are trying to get better at reading code. And this one's hard because when I am working with more early-career developers, it's hard to figure out, like, what are they seeing? How are they interpreting the code on the page? Because oftentimes, I see that they are getting stuck on the details, whereas I would like to encourage them to just be like, you don't really need to know what's going on in that method right now. Does the method name kind of communicate enough to you, like, what this thing is doing without having to understand all of the details? But my advice would be to start figuring out what to ignore [laughs] because, like you said, it's impossible to, like, hold all of that information at one time. What do you think about that advice and, like, how do you teach that to someone? JOËL: I think you're sort of hinting at two different ways of reducing the amount you have to load in your mind. The way I think about it, I think of it sort of spatially, so you can reduce the breadth of things you have to load into your head, so, realize, wait, there's all of these methods, and I don't need to know all of the methods in the file. There's only this one entry point I care about and everything downstream of that, and you just sort of prune everything off to the side, ignore it. That's not relevant right now. But there's also sort of a depth. How deep of implementation do you really need to have? Maybe you only need to know about the high-level concepts. And then, you sort of, like, do this pruning where you say, "I'm not going to go deeper than this level," because the implementation is not really relevant to what I'm trying to understand right now. I mostly need to know what are these classes and how do they interact with each other? Or something along those lines. And, ideally, you're may be doing a little bit of both. You probably don't need to go all the way to the deep implementation of every method, but you also don't necessarily need to know all of the high-level concepts and all of the objects in the system that interact. So, being able to prune in sort of both dimensions, breadth and depth, helps you to, I think, narrow the window of what you need to learn. STEPHANIE: Yeah, that's a really great point. I have a couple more strategies that I just thought about as you were talking about that. One is kind of on the journey to let go of some things that I can't understand in the moment. If they seem important, I will write them down and, like, put them somewhere in a list to come back to later and be like, "This is a thing I don't fully understand yet," and just be okay with that. I think, for me, there is some anxiety of like, oh, like, what if I'll need to know about it later? And at least putting it down somewhere as like, okay, like, I've done something with that anxious [laughs] energy of, like, recognizing that I don't understand this right now, and that's okay. But I can revisit it later. And then, another one is almost the opposite, where it's like, what are my landmarks as I'm navigating through a codebase? Like, what are the files that I'm consistently opening? Because so many of the roads lead to this object. Even when I'm kind of going through different paths, it's like, I can hook into, like, the behavior that I'm looking for from these landmark objects or models because they are really important in this domain. So, it's like, I don't necessarily need to remember every step of the way, but if I can recall some of the more important methods, then I can kind of find my way back. JOËL: Do you just try to, like, memorize those, or do you write them down? Like, how do you make a method or an object a landmark for you? STEPHANIE: That has felt a little more, like, it becomes more, like, muscle memory, I think, because I'm revisiting them pretty frequently. I don't know, it's somehow the act of repeating, like, going through those files just gets encoded somewhere in my brain [laughs], and I don't have to worry as much about forgetting them. JOËL: Strengthening that neural pathway. STEPHANIE: Yeah, exactly. JOËL: Or whatever is happening in the brain there. STEPHANIE: [laughs] JOËL: I like what you were saying earlier, though, about taking notes and sort of almost, like, a breadcrumbs approach. We did an episode almost two years ago where we talked about note-taking for various purposes and note-taking as an exploration exercise, and then note-taking when debugging, where we went deeper into that topic. And I think that would be really relevant to any of our listeners. We'll link that in the show notes. STEPHANIE: Yeah. Leaving breadcrumbs. That's a great metaphor or just a way to describe it. Because I have a little shorthand for if I am leaving myself notes in a codebase as I'm trying to understand what's happening, and it's just, like, putting my initials in a comment and, like, including some observation or commentary about what I'm seeing or a question. JOËL: Also, just a kind of meta observation here, but in the last, you know, 10-15 minutes we've been talking about this, we're already creating our own set of metaphors, and language, and mental models around understanding code. We're talking about breadcrumbs, and landmarks, and looking at code through a broad versus deep lens. That's exactly what we're talking about. STEPHANIE: Joël, do you have any mental models that you use that we haven't really gotten into yet? JOËL: I don't know if they're mental models per se, but I lean very heavily into diagramming as a form of understanding code. And maybe that's a way of sort of reducing the number of concepts because instead of now sort of thinking in terms of, like, lines of code, I'm thinking in terms of maybe some boxes and arrows, and that's a much higher-level way of looking at a system and can give me some really interesting insights. And there are a ton of different diagrams you can use for different things, and I guess all of them are based on a different maybe mental model of what a system is. So, for example, I might actually write out the method call graph starting from some endpoint and just sort of saying, "Hey, when I call this method, what are all of the methods downstream that get called? And is there anything interesting at any of those steps?" Variation on that if you're looking at, let's say, some kind of performance thing would be, like, a flame graph where you have sort of that but then it also shows you the amount of time spent in each of the methods. And that can give you a sense of where your bottlenecks are. Another one that I really like is thinking in terms of a finite state machine. So, sort of following data, how does it change in response to different events that can come into the system? And I'm not talking about, oh, you're using one of the, like, state machine gems out there for your Rails app. This is more of a way of thinking about programs and how they act. You can have just a plain, old Rails app, and you're thinking about, okay, well, how does a cart turn into an order, turn into a fulfillment request at the warehouse, turns into a tracking number for shipping? Modeling that as a state machine. And also, you know, can it move back along that path, or is it only linear move forward? Any kind of multi-state form a wizard often has paths where you move back. It's not linear. That very easily can be drawn out as a state machine. So, that is something that I really like to pull out when I'm trying to understand a, like, complex workflow. STEPHANIE: Yeah, I think we've talked about this before a little bit, or maybe not even a little bit, a lot [laughs]. But I know that you're a big fan of Mermaid.js for creating diagrams in markdown that can be embedded in a pull request description or even in a commit message. When I was hearing you talk about state machines and just all the different paths that can lead to different states, I was like, I bet that's something that you would create using a diagram and stick for yourself and others when sharing code. JOËL: Yes, Mermaid does support state machines as a graph type, which is really cool. Another thing that you can do is embed those in tools like Obsidian, which is my current note-taking tool. So, if I'm doing some sort of notes as a sort of exploratory tool, I will often start writing a Mermaid graph directly in line, and it will render and everything. That's really nice. If I'm not in Obsidian and I just need some sort of one-off graph, I'll often lean on Mermaid.live, which just gives you an editor where you can write up some Mermaid code. It will render it, and then you can copy the PNG into somewhere else and share that with other people. So, if I just need a one-off thing to share in Slack or something like that, I like to lean on that. Another type of diagram that I use pretty frequently is an entity-relationship diagram, so sort of what database tables are related to what others. On larger apps, there's just so many tables, and maybe a bunch of JOINS and things like that, and it's sometimes difficult to get the picture of what is happening, so I'll often draw out a graph of those. Now, it's not worth doing the entire database because that will be huge and overwhelming. So, I'll find, like, five or six tables that are relevant to me and then try to answer the question: How are they related to each other? STEPHANIE: Yeah, I like that. I was going to ask if you do it manually or if you use a tool because I've worked in various apps that have used the Rails ERD gem that will generate an entity-relationship diagram for you every time the schema changes. But there's something very compelling, to me, about the idea of trying to just figure out if you know the relationships, if you could draw them out, as opposed to having a tool do it for you. JOËL: Exactly. STEPHANIE: And I think, like, also, you do have information that might not be encoded in the system. Like, you actually know, oh, these two tables are related, even if no one has defined an association on them. I think that is important in understanding actually how the system is working in real life, I guess. JOËL: Agreed. So, we've been talking a lot about how we can use different tools, different mental models to take code that somebody else has written and kind of, like, almost read it from disk and load it into our brains. But what about the opposite? We're faced with a business problem, and we want to sort of write it to disk, turn it into code that somebody else will then read or that a machine will execute. I hear that happens occasionally. Are there sort of mental models or ways of approaching tackling a more, like, amorphous problem in the real world and turning that into code? Like, are they just the inverse of what we do when we read code, or are they, like, totally different set of skills? STEPHANIE: For me personally, I don't follow this framework very strictly, but I think more intuitively how I like to go about it is more behavior-driven where...because that is the language of maybe our cross-functional partners. They're saying like, "Hey, like, when this happens, I want to be able to do this," and I kind of start there. Maybe I'll pick up some of the keywords that they're repeating pretty frequently as like, oh, like, this is a concept. Actually, lately, the past couple of weeks, I've been test-driving almost all of my code as I work on a totally, like, greenfield feature. And that has been working really well for me, I think, because we did explore more granular, both, like, granular and abstract concepts when we were spiking this feature. And so, we had come up with some domain models. I had kind of thought about, like, how they might interact with each other. But when you then have to actually, like, code that, there are so many little nuances and things to keep track of that I found test driving things from, like, behavior and user stories. Those are really helpful in keeping me, like, on track to making sure that I didn't just have all these little pieces of domain concepts that then didn't really interact in a meaningful way. JOËL: Yeah, the sort of very, like, user or customer-centric approach to thinking about what is this app doing? Is a great way to think about it. And I guess the sort of translation of that, that first step of translation into code is some sort of, like, system spec. STEPHANIE: Yeah, exactly. JOËL: I like that because, you know, we have all these other abstractions that we use as developers. But at the end of the day, our customers and even, you know, our product people aren't thinking in terms of, like, objects and classes and all these other fun abstractions that we have. They're thinking in terms of behaviors and, you know, maybe subsystems, workflows, things like that. And then it's up to us to translate that into whatever paradigm of our language that we're using. STEPHANIE: Do you do things differently from me? JOËL: I don't think that I do it necessarily differently. I think it's one of several tools I have in my tool belt. Something that is similar but from a slightly different angle is inspiring myself with a lot of the ideas from domain-driven design. You know, we've been talking a lot about this idea of, like, mental models and having a vocabulary, things like that, about sort of the way that we work, but that exists at the product level as well. And what if we could encode a lot of that into our application itself? So, is there a distinction between a subscriber and a payer in our system? Is there specialized vocabulary around different other concepts in the app? Maybe instead of just having those be things that product people talk about, what if we made them actual named entities in the system and have maybe our object graph, at least in some way, reflect the sort of idealized model of what our business actually does? That often means that you're thinking of things at a higher level because you're thinking of things at the level that our product people are thinking about them. You might be thinking of things in terms of user journeys, or product workflows, or things like that, because you say, "Oh, well, a new payer has been added to this group account. And that has started a subscription, which then means that a user has access to these corporate features that they didn't have when they were in a solo account." Like, I've just thrown ten different sort of product terms out there that, you know, if there are concepts in our code can help us think about less of the implementation. What does the app do, or how does the app do it? And more in terms of, like, product terms, what does the app do? How do people experience the behavior, or maybe how does data change over the life cycle of the app? So, those perspectives, I think, have helped me distill down sort of more vague product ideas into things that I can then start turning into code. STEPHANIE: Absolutely. I think one way that this framework ends up falling short, at least for me a little bit sometimes, is making connections between behaviors that are similar but not exactly the same. Or when you think about them in more isolated ways, like, it's easy to miss that, like, they are the same idea and that there is, like, something a bit higher level that you can connect them, that you can create a more abstract class for, even though that's not actually how people talk about the things. One example I can think of is things like concerns that are both related to domain language but then also, like, kind of specific to how things work in the code as a system because you might not necessarily call something a subscribable from a product perspective. Do you have any thoughts about identifying those pieces? JOËL: So, what's interesting is I think there's a little bit of, like, layers above and below, the sort of domain layer where you're talking in terms of, like, what the product team would use. When you're doing a lot of the implementation, there will be things that are just, like, that's how we implemented them. They're in the nitty gritty, and they're not terms that the product team would necessarily use. Things like array and string they're low-level details. We have to use them. That's not really relevant to the world of payers, and subscribers, and things like that. So, they're sort of lower layer. And I think that's totally fine to have things where we sort of have things that are sort of programmer only, as long as they're sort of contained within this higher-level layer because that allows people new to the app to sort of see what are the different things in the application to think about things in a higher level. It also allows for smoother communication with the product team. So, ideally, you don't have a concept in the app that is the same as something that the product team, but you just both gave it different names, and then that's really annoying. Or maybe the dev team created something that's, like, almost exactly the same as what the product team talks about, but with some, like, slight variations. Now, you're just going to be talking past each other in every planning meeting, and that will be incredibly annoying. STEPHANIE: Yeah. At one point, when I was trying to communicate, like, async about how a feature works, and there was like the product word for it and then the dev word for it, I would have to type out both [chuckles] because I wanted to make sure that no one was confused about what we were talking about, which was the same thing that just had two names. And yeah, I don't know how many seconds of my life I'll never get back as a result [chuckles]. JOËL: Were these concepts that were identical and had just different names, or was this like, oh, well, our internal subscribed user is almost the same as when product talks about and, I don't know, employee, but our subscribed user has a couple of other extra behaviors that employees don't have, and now there's, like, this weird, like, overlap? STEPHANIE: Yeah, both situations I have found myself in, but I think this one they were virtually identical. Like, they could be used interchangeably to mean the same thing by people who understand both of those definitions, but the problem was that we still had two words [laughs]. JOËL: Yeah, yeah. I'm a big fan of, where possible, converging on the product team's definition. Although because code forces you to be more precise, sometimes that can then force some conversations with the product team about, like, "Hey, so we've been hand waving around this concept of a subscriber. Turns out we think there's actually two different kinds of concepts at work here: the person who's consuming the content and the person who's paying for it. And are they really the same thing, or should we sort of think about these as two different entities? And, in that case, what should the name be?" And that can force a really, I think, healthy conversation between development and product. STEPHANIE: Yeah, I like that. You mentioned there was, like, a higher level and a lower level, but I don't think we've gotten to the higher one yet. JOËL: Yeah. Sometimes, you want to build abstraction sort of over. You're talking about the idea of, like, subscribable things. I think that's where I'm a lot fuzzier. It's much more case-by-case. Where possible, I'd like to introduce some of those things as domain vocabulary so that we'd say, "Well, look, we have a, like, family of products, and they're all subscribable." And maybe, like, the adjective doesn't matter quite as much to our product people, but, you know, because we're using a module in Ruby, we want to lean into the adjective form, and that's fine. But I would at least want some loose connection there. STEPHANIE: Yeah, that makes sense because I think that ultimately makes for a better product. If we're thinking about, like, how to present a hierarchy of information to a user, like a navigation menu, we would want to group those things that are under that family together, ideally, so that they know how to interact with it. JOËL: Another thing that I think falls maybe under, like, this higher-level umbrella are things like design patterns. So, maybe because we want to be able to sort of, like, swap things in and out, we're using some form of strategy pattern. That feels like maybe it's a little bit higher level. It interacts with a lot of the domain concepts, but our product team doesn't really need to think in terms of, like, oh, strategies, and swappable things, and, like, flex points in your architecture. So, those would not necessarily be domain vocabulary. Although I could see, like, maybe there's a way where they do get a domain name, and that's great. STEPHANIE: Oh, I think maybe this is where I disagree with you a little bit. Well, actually, I agreed with what you said at the end [laughs] in terms of how maybe they should be part of the domain vocabulary because I think...I've seen product not fully understand the complexity of the application as it grows over time. And that can lead to sometimes, like, not as great product experience or experience for the user, like, interacting with this product. And maybe that is something we want to, as developers, if we're starting to see and feel and have maybe even introduced a pattern for...I can't claim to have done this too much, but it's definitely a skill I want to hone in on. But, like, how do I communicate to product folks so that we understand, oh, like, where is it possible for these different types of a subscriber to diverge? Because that is important, I think, in determining the future of a product and, like, where we want to invest in it and where we should focus, like, new features. JOËL: And oftentimes, when there is that kind of divergence, there probably will be some sort of product-level thinking that needs to happen there. Are we saying, "Hey, we have one of three types of subscribers, and we want to think about that"? Or maybe we want to say, "We have three different ways of processing an application." Maybe it's derived automatically. Maybe it's a dropdown that you have to pick. But let's say it's a dropdown. What do we name that dropdown with the, like, kind of processing that we want to do to an application? The thing that we want to name that dropdown that's probably a good name for that, like, group of strategies, assuming we implement with a strategy pattern. Maybe we're doing it differently. STEPHANIE: Yeah. The more you talk about that, the more I'm convinced that that's, like, the way I want to be working at least, because you have to know what's there in order to, like, name it. You know, you have to face it, essentially [laughs]. Whereas I think a lot of applications I've worked on fall into the trap of all of those things are obscured way down in the depths of the user flow, where it's like, oh, suddenly, for some reason, you can, like, have a dropdown here that totally changes the behavior, even though you've gotten this far in either the stack trace or even just, like the user journey, as I know you like to branch early in your code. JOËL: [laughs]. STEPHANIE: But you should also branch early from a user's experience [laughs]. JOËL: In general, I'm just a big fan of having a communication loop between development and product, not only sort of receiving a lot of useful information from the product team about what we want to build. But then because we're encountering this more, like, technical spec that we're writing, have those conversations bubble back to product and say, "Hey, so we talked about a dropdown where there are sort of three different ways of processing an application. Let's talk a little bit more about what it means to have three different ways of processing. And what do we want to name that? Is that accessible to everyone, or are they sort of one-to-one tied with a type of user?" And all of a sudden, that has just generated probably a lot of questions that product never even thought to ask because they're working on an infinite canvas of possibilities. And it's really helped you as a developer to have better names to write your code and sort of better sketch out the boundaries of the problem you're trying to solve. So, I think it's a really healthy loop to have. I strongly encourage it. So, we've spent a lot of time talking about thinking about behavior and things like the domain-driven design movement. But a few other things I want to shout out as being really helpful, one is an exercise where you take a problem statement and just underline all of the nouns. That is a great way to get a sense of, like, what is going on here. More generally, I think a lot of what we're talking about falls under the umbrella of what you might call analysis. And so, digging into different analytic techniques can be a great way to better understand the problem that you're working through. One such tool would be decision tables. So, you have a problem, and you say, "Well, given these inputs, what should the outputs be?" STEPHANIE: Cool. If there were any techniques or tools that we missed in terms of how you load code in your brain or generate code from your brain [laughs], we would love to know. You can write in to us at hosts@bikeshed.fm. JOËL: On that note, shall we wrap up? STEPHANIE: Let's wrap up. STEPHANIE: Show notes for this episode can be found at bikeshed.fm. JOËL: This show has been produced and edited by Mandy Moore. STEPHANIE: If you enjoyed listening, one really easy way to support the show is to leave us a quick rating or even a review in iTunes. It really helps other folks find the show. JOËL: If you have any feedback for this or any of our other episodes, you can reach us @_bikeshed, or you can reach me @joelquen on Twitter. STEPHANIE: Or reach both of us at hosts@bikeshed.fm via email. JOËL: Thanks so much for listening to The Bike Shed, and we'll see you next week. ALL: Byeeeeeeee!!!!!!! AD: Did you know thoughtbot has a referral program? If you introduce us to someone looking for a design or development partner, we will compensate you if they decide to work with us. More info on our website at: tbot.io/referral. Or you can email us at: referrals@thoughtbot.com with any questions.

    422: Listener Topics Grab Bag

    Play Episode Listen Later Apr 9, 2024 35:23


    Joël conducted a thoughtbot mini-workshop on query plans, which Stephanie found highly effective due to its interactive format. They then discuss the broader value of interactive workshops over traditional talks for deeper learning. Addressing listener questions, Stephanie and Joël explore the strategic use of if and else in programming for clearer code, the importance of thorough documentation in identifying bugs, and the use of Postgres' EXPLAIN ANALYZE, highlighting the need for environment-specific considerations in query optimization. Episode mentioning query plans (https://bikeshed.thoughtbot.com/418) Query plan visualizer (https://explain.dalibo.com/) RailsConf 2024 (https://railsconf.org/) Episode 349: Unpopular Opinions (https://bikeshed.thoughtbot.com/349) Squint test (https://www.youtube.com/watch?v=8bZh5LMaSmE) Episode 405: Retro on Sandi Metz rules (https://bikeshed.thoughtbot.com/405) Structuring conditionals in a wizard (https://thoughtbot.com/blog/structuring-conditionals-in-a-wizard) Episode 417: Module docs (https://bikeshed.thoughtbot.com/417) Episode 416: Multidimensional numbers (https://bikeshed.thoughtbot.com/416) ruby-units gem (https://github.com/olbrich/ruby-units) Solargraph (https://marketplace.visualstudio.com/items?itemName=castwide.solargraph) parity (https://github.com/thoughtbot/parity) Transcript: STEPHANIE:  Hello and welcome to another episode of The Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I'm Stephanie Minn. JOËL: And I'm Joël Quenneville, and together, we're here to share a bit of what we've learned along the way. STEPHANIE: So, Joël, what's new in your world? JOËL: Just recently, I ran a sort of mini workshop for some colleagues here at thoughtbot to dig into the idea of query plans and, how to read them, how to use them. And, initially, this was going to be more of a kind of presentation style. And a colleague and I who were sharing this decided to go for a more interactive format where, you know, this is a, like, 45-minute slot. And so, we set it up so that we did a sort of intro to query plans in about 10 minutes then 15 minutes of breakout rooms, where people got a chance to have a query plan. And they had some sort of comprehension questions to answer about it. And then, 15 minutes together to have each group share a little bit about what they had discovered in their query plan back with the rest of the group, so trying to balance some understanding, some application, some group discussion, trying to keep it engaging. It was a pretty fun approach to sharing information like that. STEPHANIE: Yeah. I wholeheartedly agree. I got to attend that workshop, and it was really great. Now that I'm hearing you kind of talk about the three different components and what you wanted people attending to get out of it, I am impressed because [laughs] there is, like, a lot more thought, I think, that went into just participant engagement that reflecting on it now I'm like, oh yeah, like, I think that was really effective as opposed to just a presentation. Because you had, you know, sent us out into breakout rooms, and each group had a different query that they were analyzing. You had kind of set up links that had the query set up in the query analyzer. I forget what the tool was called that you used. JOËL: I forget the name of it, but we will link it in the show notes. STEPHANIE: Yeah. It was helpful for me, though, because, you know, I think if I were just to have learned about it in a presentation or even just looked at, you know, screenshots of it on a slide, that's different still from interacting with it and feeling more confident to use it next time I find myself in a situation where it might be helpful. JOËL: It's really interesting because that was sort of the goal of it was to make it a bit more interactive and then, hopefully, helping people to retain more information than just a straight up, like, presentation would be. I don't know how you feel, I find that often when I go to a place like, let's say, RailsConf, I tend to stay away from more of the workshop-y style events and focus more on the talks. Is that something that you do as well? STEPHANIE: Yeah. I have to confess that I've never attended a workshop [laughs] at a conference. I think it's partly my learning style and also partly just honestly, like, my energy level when I'm at the conference. I kind of just want to sit back. It's on my to-do list. Like, I definitely want to attend one just to see what it's like. And maybe that might even inspire me to want to create my own workshop. But it's like, once I'm in it, and, you know, like, everyone else is also participating, I'm very easily peer pressured [laughs]. So, in a group setting, I will find myself enjoying it a lot more. And I felt that kind of same way with the workshop you ran for our team. Though, I will say a funny thing that happened was that when I went out into my breakout group with another co-worker, and we were trying to grok this query that you gave us, we found out that we got the hardest one, the most complicated one [laughs] because there were so many things going on. There was, like, multiple, like, you know, unions, some that were, like, nested, and then just, like, a lot of duplication as well, like, some conditions that were redundant because of a different condition happening inside of, like, an inner statement. And yeah, we were definitely scratching our heads for a bit and were very grateful that we got to come back together as a group and be like, "Can someone please help? [laughs] Let's figure out what's going on here." JOËL: Sort of close that loop and like, "Hey, here's what we saw. What does everybody else see?" STEPHANIE: Yeah, and I appreciated that you took queries from actual client projects that you were working on. JOËL: Yeah, that was the really fun part of it was that these were not sort of made-up queries to illustrate a point. These were actual queries that I had spent some time trying to optimize and where I had had to spend a lot of time digging into the query plans to understand what was going on. And it sounds like, for you, workshops are something that is...they're generally more engaging, and you get more value out of them. But there's higher activation energy to get started. Does that sound right? STEPHANIE: Yeah, that sounds right. I think, like, I've watched so many talks now, both in person and on YouTube, that a lot of them are easily forgettable [laughs], whereas I think a workshop would be a lot more memorable because of that interactivity and, you know, you get out of it what you put in a little bit. JOËL: Yeah, that's true. Have you looked at the schedule for RailsConf 2024 yet? And are there any workshops on there that you're maybe considering or that maybe have piqued your interest? STEPHANIE: I have, in fact, and maybe I will check off attending a workshop [laughs] off my bucket list this year. There are two that I'm excited about. Unfortunately, they're both at the same time slot, so I -- JOËL: Oh no. You're going to have to choose. STEPHANIE: I know. I imagine I'll have to choose. But I'm interested in the Let's Extend Rails With A Gem by Noel Rappin and Vision For Inclusion Workshop run by Todd Sedano. The Rails gem one I'm excited about because it's just something that I haven't had to do really in my dev career so far, and I think I would really appreciate having that guidance. And also, I think that would be motivation to just get that, like, hands-on experience with it. Otherwise, you know, this is something that I could say that I would want to do and then never get [chuckles] around to it. JOËL: Right, right. And building a gem is the sort of thing that I think probably fits better in a workshop format than in a talk format. STEPHANIE: Yeah. And I've really appreciated all of Noel's content out there. I've found it always really practical, so I imagine that the workshop would be the same. JOËL: So, other than poring over the RailsConf schedule and planning your time there, what has been new for you this week? STEPHANIE: I have a really silly one [laughs]. JOËL: Okay. STEPHANIE: Which is, yesterday I went out to eat dinner to celebrate my partner's birthday, and I experienced, for the first time, robots [laughter] at this restaurant. So, we went out to Hot Pot, and I guess they just have these, like, robot, you know, little, small dish delivery things. They were, like, as tall as me, almost, at least, like, four feet. They were cat-themed. JOËL: [laughs] STEPHANIE: So, they had, like...shaped like cat...they had cat ears, and then there was a screen, and on the screen, there was, like, a little face, and the face would, like, wink at you and smile. JOËL: Aww. STEPHANIE: And I guess how this works is we ordered our food on an iPad, and if you ordered some, like, side dishes and stuff, it would come out to you on this robot cat with wheels. JOËL: Very fun. STEPHANIE: This robot tower cat. I'm doing a poor job describing it because I'm still apparently bewildered [laughs]. But yeah, I was just so surprised, and I was not as...I think I was more, like, shocked than delighted. I imagine other people would find this, like, very fun. But I was a little bit bewildered [laughs]. The other thing that was very funny about this experience is that these robots were kind of going down the aisle between tables, and the aisles were not quite big enough for, like, two-way traffic. And so, there were times where I would be, you know, walking up to go use the restroom, and I would turn the corner and find myself, like, face to face with one of these cat robot things, and, like, it's starting to go at me. I don't know if it will stop [laughs], and I'm the kind of person who doesn't want to find out. JOËL: [laughs] STEPHANIE: So, to avoid colliding with this, you know, food delivery robot, I just, like, ran away from it [laughs]. JOËL: You don't know if they're, like, programmed to yield or something like that. STEPHANIE: Listen, it did not seem like it was going to stop. JOËL: [laughs] STEPHANIE: It got, like, I was, you know, kind of standing there frozen in paralysis [laughs] for a little while. And then, once it got, I don't know, maybe two or three feet away from me, I was like, okay, like, this is too close for comfort [laughs]. So, that was my, I don't know, my experience at this robot restaurant. Definitely starting to feel like I'm in the, I don't know, is this the future? Someone, please let me know [laughs]. JOËL: Is this a future that you're excited or happy about, or does this future seem a little bit dystopian to you? STEPHANIE: I was definitely alarmed [laughter]. But I'm not, like, a super early adopter of new technology. These kinds of innovations, if you will, always surprise me, and I'm like, oh, I guess this is happening now [laughs]. And I will say that the one thing I did not enjoy about it is that there was not enough room to go around this robot. It definitely created just pedestrian traffic issues. So, perhaps this could be very cool and revolutionary, but also, maybe design robots for humans first. JOËL: Or design your dining room to accommodate your vision for the robots. I'm sure that flying cars and robots will solve all of this, for sure. STEPHANIE: Oh yeah [laughter]. Then I'll just have to worry about things colliding above my head. JOËL: And for the listeners who cannot see my face right now, that was absolutely sarcasm [laughs]. Speaking of our listeners, today we're going to look at a group of different listener questions. And if you didn't know that, you could send in a question to have Stephanie and I discuss, you can do that. Just send us an email at hosts@bikeshed.fm. And sometimes, we put it into a regular episode. Sometimes, we combine a few and sort of make a listener question episode, which is what we're doing today. STEPHANIE: Yeah. It's a little bit of a grab bag. JOËL: Our first question comes from Yuri, and Yuri actually has a few different questions. But the first one is asking about Episode 349, which is pretty far back. It was my first episode when I was coming on with Chris and Steph, and they were sort of handing the baton to me as a host of the show. And we talked about a variety of hot takes or unpopular opinions. Yuri mentions, you know, a few that stood out to him: one about SPAs being not so great, one about how you shouldn't need to have a side project to progress in your career as a developer, one about developer title inflation, one about DRY and how it can be dangerous for a mid-level dev, avoiding let in RSpec specs, the idea that every if should come with an else, and the idea that developers shouldn't be included in design and planning. And Yuri's question is specifically the question about if statements, that every if should come with an else. Is that still an opinion that we still have, and why do we feel that way? STEPHANIE: Yeah, I'm excited to get into this because I was not a part of that episode. I was a listener back then when it was still Steph and Chris. So, I am hopefully coming in with a different, like, additional perspective to add as well while we kind of do a little bit of a throwback. So, the one about every if should come with an else, that was an unpopular opinion of yours. Do you mind kind of explaining what that means for you? JOËL: Yeah. So, in general, Ruby is an expression-oriented language. So, if you have an if that does not include an else, it will implicitly return nil, which can burn you. There may be some super expert programmers out there that have never run into undefined method for nil nil class, but I'm still the kind of programmer who runs into that every now and then. And so, implicit nils popping up in my code is not something I generally like. I also generally like having explicit else for control flow purposes, making it a little bit clearer where flow of control goes and what are the actual paths through a particular method. And then, finally, doing ifs and elses instead of doing them sort of inline or as trailing conditionals or things like that, by having them sort of all on each lines and balancing out. The indentation itself helps to scan the code a little bit more. So, deeper indentation tells you, okay, we're, like, nesting multiple conditions, or something like that. And so, it makes it a little bit easier to spot complexity in the code. You can apply, and I want to say this is from Sandi Metz, the squint test. STEPHANIE: Yeah, it is. JOËL: Where you just kind of, like, squint at your code so you're not looking at the actual characters, and more of the structure, and the indentation is actually a friend there rather than something to fight. So, that was sort of the original, I think, idea behind that. I'm curious, in your experience, if you would like to balance your conditionals, ifs with something else, or if you would like to do sort of hanging ifs. STEPHANIE: Hanging ifs, I like that phrase that you just coined there. I agree with your opinion, and I think it's especially true if you're returning values, right? I mean, in Ruby, you kind of always are. But if you are caring about return values, like you said, to avoid that implicit nil situation, I find, especially if you're writing tests for that code, it's really easy, you know, if you spot that condition, you're like, okay, great. Like, this is a path I need to test. But then, oftentimes, you don't test that implicit path, and if you don't enter the condition, then what happens, right? So, I think that's kind of what you're referring to when you talk about both. It's, like, easier to spot in terms of control flow, like, all the different paths of execution, as well as, yeah, like, saving you the headaches of some bugs down the line. One thing that I thought about when I was kind of revisiting that opinion of yours is the idea of like, what are you trying to communicate is different or special about this condition when you are writing an if statement? And, in my head, I kind of came up with a few different situations you might find yourself in, which is, one, where you truly just have, like, a special case, and you're treating that completely differently. Another when you have more of a, like, binary situation, where it's you want to kind of highlight either...more of a dichotomy, right? It's not necessarily that there is a default but that these are two opposite things. And then, a third situation in which you have multiple conditions, but you only happen to have two right now. JOËL: Interesting. And do you think that, like, breaking down those situations would lead you to pick different structures for writing your conditionals? STEPHANIE: I think so. JOËL: Which of those scenarios do you think you might be more likely to reach for an if that doesn't have an else that goes with it? STEPHANIE: I think that first one, the special case one. And in Yuri's email, he actually asked, as a follow-up, "Do we avoid guard clauses as a result of this kind of heuristic or rule?" And I think that special case situation is where a guard clause would shine because you are highlighting it by putting it at the top of a method, and then saying like, you know, "Bail out of this" or, like, "Return this particular thing, and then don't even bother about the rest of this code." JOËL: I like that. And I think guard clauses they're not the first thing I reach for, but they're not something I absolutely avoid. I think they need to be used with care. Like you said, they have to be in the top of your method. If you're adding returns and things that break out of your method, deep inside a conditional somewhere, 20 lines into your method, you don't get to call that a guard clause anymore. That's something else entirely. I think, ideally, guard clauses are also things that will break out of the method, so they're maybe raising exception. Maybe they're returning a value. But they are things that very quickly check edge cases and bail so that the body of the method can focus on expecting data in the correct shape. STEPHANIE: I have a couple more thoughts about this; one is I'm reminded of back when we did that episode on kind of retroing Sandi Metz's Rules For Developers. I think one of the rules was: methods should only be five lines of code. And I recall we'd realized, at least I had realized for the first time, that if you write an if-else condition in Ruby, that's exactly five lines [laughs]. And so, now that I'm thinking about this topic, it's cool to see that a couple of these rules converge a little bit, where there's a bit of explicitness in saying, like, you know, if you're starting to have more conditions that can't just be captured in a five-line if-else statement, then maybe you need something else there, right? Something perhaps like polymorphic or just some way to have branched earlier. JOËL: That's true. And so, even, like, you were talking about the exceptional edge cases where you might want to bail. That could be a sign that your method is doing too much, trying to like, validate inputs and also run some sort of algorithm. Maybe this needs to be some sort of, like, two-step thing, where there's almost, like, a parsing phase that's handled by a different object or a different method that will attempt to standardize your inputs and raise the appropriate errors and everything. And then, your method that has the actual algorithm or code that you're trying to run can just assume that its inputs are in the correct shape, kind of that pushing the uncertainty to the edges. And, you know, if you've only got one edge case to check, maybe it's not worth to, like, build this in layers, or separate out the responsibilities, or whatever. But if you're having a lot, then maybe it does make sense to say, "Let's break those two responsibilities out into two places." STEPHANIE: Yeah. And then, the one last kind of situation I've observed, and I think you all talked about this in the Unpopular Opinions episode, but I'm kind of curious how you would handle it, is side effects that only need to be applied under a certain condition. Because I think that's when, if we're focusing less on return values and more just on behavior, that's when I will usually see, like, an if something, then do this that doesn't need to happen for the other path. JOËL: Yes. I guess if you're doing some sort of side effect, like, I don't know, making a request to an API or writing to a file or something, having, like, else return nil or some other sentinel value feels a little bit weird because now you're caring about side effects rather than return values, something that you need to keep thinking of. And that's something where I think my thing has evolved since that episode is, once you start having multiple of these, how do they compose together? So, if you've got if condition, write to a file, no else, keep going. New if condition, make a request to an API endpoint, no else, continue. What I've started calling independent conditions now, you have to think about all the different ways that they can combine, and what you end up having is a bit of a combinatorial explosion. So, here we've got two potential actions: writing to a file, making a request to an API. And we could have one or the other, or both, or neither could happen, depending on the inputs to your method, and maybe you actually want that, and that's cool. Oftentimes, you didn't necessarily want all of those, especially once you start going to three, four, five. And now you've got that, you know, explosion, like, two to the five. That's a lot of paths through your method. And you probably didn't really need that many. And so, that can get really messy. And so, sometimes the way that an if and an else work where those two paths are mutually exclusive actually cuts down on the total number of paths through your method. STEPHANIE: Hmm, I like that. That makes a lot of sense to me. I have definitely seen a lot of, like, procedural code, where it becomes really hard to tell how to even start relating some of these concepts together. So, if you happen to need to run a side effect, like writing to a file or, I don't know, one common one I can think of is notifying something or someone in a particular case, and maybe you put that in a condition. But then there's a different branching path that you also need to kind of notify someone for a similar reason, maybe even the same reason. It starts to become hard to connect, like, those two reasons. It's not something that, like, you can really scan for or, like, necessarily make that connection because, at that point, you're going down different paths, right? And there might be other signals that are kind of confusing things along the way. And it makes it a lot harder, I think, to find a shared abstraction, which could ultimately make those really complicated nested conditions a little more manageable or just, like, easier to understand at a certain complexity. I definitely think there is a threshold. JOËL: Right. And now you're talking about nested versus non-nested because when conditions are sort of siblings of each other, an if-else behaves differently from two ifs without an else. I think a classic situation where this pops up is when you're structuring code for a wizard, a multi-step form. And, oftentimes, people will have a bunch of checks. They're like, oh, if this field is present, do these things. If this field is present, do these things. And then, it becomes very tricky to know what the flow of control is, what you can expect at what moment, and especially which actions might get shared across multiple steps. Is it safe to refactor in one place if you don't want to break step three? And so, learning to think about the different paths through your code and how different conditional structures will impact that, I think, was a big breakthrough for me in terms of taking the next logical step in terms of thinking, when do I want to balance my ifs and when do I not want to? I wrote a whole article on the topic. We'll link it in the show notes. So, Yuri, thanks for a great question, bringing us back into a classic developer discussion. Yuri also asks or gives us a bit of a suggestion: What about revisiting this topic and doing an episode on hot takes or unpopular topics? Is that something that you'd be interested in, Stephanie? STEPHANIE: Oh yeah, definitely, because I didn't get to, you know, share my hot topics the last episode [laughs]. [inaudible 24:23] JOËL: You just got them queued up and ready to go. STEPHANIE: Yeah, exactly. So, yeah, I will definitely be brainstorming some spicy takes for the show. JOËL: So, Yuri, thanks for the questions and for the episode suggestion. STEPHANIE: So, another listener, Kevin, wrote in to us following up from our episode on Module Docs and about a different episode about Multi-dimensional Numbers. And he mentioned a gem that he maintains. It's called Ruby Units. And it basically handles the nitty gritty of unit conversions for you, which I thought was really neat. He mentioned that it has a numeric class, and it knows how to do math [laughs], which I would find really convenient because that is something that I have been grateful not to have to really do since college [laughs], at least those unit conversions and all the things that I'd probably learned in math and physics courses [laughs]. So, I thought that was really cool, definitely is one to check out if you frequently work with units. It seemed like it would be something that would make sense for a domain that is more science or deals in that kind of domain. JOËL: I'm always a huge fan of anything that tags raw numbers that you're working with with a quantity rather than just floating raw numbers around. It's so easy to make a mistake to either treat a number as a quantity you didn't think of, or make some sort of invalid operation on it, or even to think you have a value in a different size than you do. You think you're dealing with...you know you have a time value, but you think it's in seconds. It's actually in milliseconds. And then, you get off by some big factor. These are all mistakes that I have personally made in my career, so leaning on a library to help avoid those mistakes, have better information hiding for the things that really aren't relevant to the work that I'm trying to do, and also, kind of reify these ideas so that they have sort of a name, and they're, like, their own object, their own thing that we can interact with in the app rather than just numbers floating around, those are all big wins from my perspective. STEPHANIE: I also just thought of a really silly use case for this that is, I don't know, maybe I'll have to experiment with this. But every now and then, I find the need to have to convert a unit, and I just pop into Google, and I'm like, please give me, you know, I'll search for 10 kilometers in miles or something [laughs]. But then I have to...sometimes Google will figure it out for me, and sometimes it will just list me with a bunch of weird conversion websites that all have really old-school UI [laughs]. Do you know what I'm talking about here? Anyway, I would be curious to see if I could use this gem as a command-line interface [laughs] for me without having to go to my browser and roll the dice with freecalculator.com or something like that [laughs]. JOËL: One thing that's really cool with this library that I saw is the ability to define your own units, and that's a thing that you'll often encounter having to deal with values that are maybe not one of the most commonly used units that are out there, dealing with numbers that might mean a thing that's very particular to your domain. So, that's great that the library supports that. I couldn't see if it supports multi-dimensional units. That was the episode that inspired the comment. But either way, this is a really cool library. And thank you, Kevin, for sharing this with us. STEPHANIE: Kevin also mentions that he really enjoys using YARD docs. And we had done that whole episode on Module Docs and your experience writing them. So, you know, your people are out there [laughs]. JOËL: Yay. STEPHANIE: And we talked about this a little bit; I think that writing the docs, you know, on one hand, is great for future readers, but, also, I think has the benefit of forcing the author to really think about their inputs and outputs, as Kevin mentions. He's found bugs by simply just going through that process in designing his code, and also recommends Solargraph and Solargraph's VSCode extension, which I suspect really kind of makes it easy to navigate a complex codebase and kind of highlight just what you need to know when working with different APIs for your classes. So, I recently kind of switched to the Ruby LSP, build with Shopify, but I'm currently regretting it because nothing is working for me right now. So, that might be the push that I need [laughs] to go back to using Solargraph. JOËL: It's interesting that Kevin mentions finding bugs while writing docs because that has absolutely been my experience. And even in this most recent round, I was documenting some code that was just sort of there. It wasn't new code that I was writing. And so, I had given myself the rule that this would be documentation-only PRs, no code changes. And I found some weird code, and I found some code that I'm 98% sure is broken. And I had to have the discipline to just put a notice in the documentation to be like, "By the way, this is how the method should work. I'm pretty sure it's broken," and then, maybe come back to it later to fix it. But it's amazing how trying to document code, especially code that you didn't write yourself, really forces you to read it in a different way, interact with it in a different way, and really, like, understand it in a deep way that surprised me a little bit the first time I did it. STEPHANIE: That's cool. I imagine it probably didn't feel good to be like, "Hey, I know that this is broken, and I can't fix it right now," but I'm glad you did. That takes a lot of, I don't know, I think, courage, in my opinion [laughs], to be like, "Yeah, I found this, and I'm going to, you know, like, raise my hand acknowledging that this is how it is," as supposed to just hiding behind a broken functionality that no one [laughs] has paid attention to. JOËL: And it's a thing where if somebody else uses this method and it breaks in a way, and they're like, "Well, the docs say it should behave like this," that would be really frustrating. If the docs say, "Hey, it should behave like this, but it looks like it's broken," then, you know, I don't know, I would feel a little bit vindicated as a person who's annoyed at the code right now. STEPHANIE: For sure. JOËL: Finally, we have a message from Tim about using Postgres' EXPLAIN ANALYZE. Tim says, "Hey, Joël, in the last episode, you talked a bit about PG EXPLAIN ANALYZE. As you stated, it's a great tool to help figure out what's going on with your queries, but there is a caveat you need to keep in mind. The query planner uses statistics gathered on the database when making decisions about how to fetch records. If there's a big difference between your dev or staging database and production, the query may make different decisions. For example, if a table has a low number of records in it, then the query planner may just do a table scan, but in production, it might use an index. Also, keep in mind that after a schema changes, it may not know about new indexes or whatever unless an explicit ANALYZE is done on the table." So, this is really interesting because, as Tim mentions, EXPLAIN ANALYZE doesn't behave exactly the same in production versus in your local development environment. STEPHANIE: When you were trying to optimize some slow queries, where were you running the ANALYZE command? JOËL: I used a combination. I mostly worked off of production data. I did a little bit on a staging database that had not the same amount of records and things. That was pretty significant. And so, I had to switch to production to get realistic results. So, yes, I encountered this kind of firsthand. STEPHANIE: Nice. For some reason, this comment also made me think of..., and I wanted to plug a thoughtbot shell command that we have called Parity, which lets you basically download your production database into your local dev database if you use Heroku. And that has come in handy for me, obviously, in regular development, but would be really great in this use case. JOËL: With all of the regular caveats around security, and PII, and all this stuff that come with dealing with production data. But if you're running real productions on production, you should be cleared and, like, trained for access to all of that. I also want to note that the queries that you all worked with on Friday are also from the production database. STEPHANIE: Really? JOËL: So, you got to see what it actually does, what the actual timings were. STEPHANIE: I'm surprised by that because we were using, like, a web-based tool to visualize the query plans. Like, what were you kind of plugging into the tool for it to know? JOËL: So, the tool accepts a query plan, which is a text output from running a SQL query. STEPHANIE: Okay. So, it's just visualizing it. JOËL: Correct. Yeah. So, you've got this query plan, which comes back as this very intimidating block of, like, text, and arrows, and things like that. And you plug it into this web UI, and now you've got something that is kind of interactive, and visual, and you can expand or collapse nodes. And it gives you tooltips on different types of information and where you're spending the most time. So, yeah, it's just a nicer way to visualize that data that comes from the query plan. STEPHANIE: Gotcha. That makes sense. JOËL: So, that's a very important caveat. I don't think that's something that we mentioned on the episode. So, thank you, Tim, for highlighting that. And for all of our listeners who were intrigued by leaning into EXPLAIN ANALYZE and query plan viewers to debug your slow queries, make sure you try it out in production because you might get different results otherwise. STEPHANIE: So, yeah, that about wraps up our listener topics in recent months. On that note, Joël, shall we wrap up? JOËL: Let's wrap up. STEPHANIE: Show notes for this episode can be found at bikeshed.fm. JOËL: This show has been produced and edited by Mandy Moore. STEPHANIE: If you enjoyed listening, one really easy way to support the show is to leave us a quick rating or even a review in iTunes. It really helps other folks find the show. JOËL: If you have any feedback for this or any of our other episodes, you can reach us @_bikeshed, or you can reach me @joelquen on Twitter. STEPHANIE: Or reach both of us at hosts@bikeshed.fm via email. JOËL: Thanks so much for listening to The Bike Shed, and we'll see you next week. ALL: Byeeeeeeeee!!!!!!! AD: Did you know thoughtbot has a referral program? If you introduce us to someone looking for a design or development partner, we will compensate you if they decide to work with us. More info on our website at: tbot.io/referral. Or you can email us at: referrals@thoughtbot.com with any questions.

    421: The Idealistic vs. Pragmatic Programmer

    Play Episode Listen Later Apr 2, 2024 41:01


    Stephanie revisits the concept of "spiking"—a phase of exploration to determine the feasibility of a technical implementation or to address unknowns in feature requests—sharing her recent experiences with a legacy Rails application. Joël brings a different perspective by discussing his involvement with a client project that heavily utilizes the dry-rb suite of gems, highlighting the learning curve associated with adapting to new patterns and libraries. Joël used to be much more idealistic and has moved to be more pragmatic. Stephanie has moved the other way. So together, Stephanie and Joël engage in a philosophical discussion on being an idealistic versus a pragmatic programmer. They explore the concept of programming as a blend of science and art, where technical decisions are not only about solving problems but also about expressing ideas and building shared understandings within a team. Spike tasks episode (https://bikeshed.thoughtbot.com/414) dry-rb (https://dry-rb.org/) Working with Maybe talk (https://www.youtube.com/watch?v=43eM4kNbb6c) Problem solving with maybe (https://thoughtbot.com/blog/problem-solving-with-maybe) Programming as Theory Building (https://pablo.rauzy.name/dev/naur1985programming.pdf) The Pragmatic Programmer (https://pragprog.com/titles/tpp20/the-pragmatic-programmer-20th-anniversary-edition/) Transcript:  JOËL: Hello and welcome to another episode of the Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I'm Joël Quenneville. STEPHANIE: And I'm Stephanie Minn, and together, we're here to share a bit of what we've learned along the way. JOËL: So, Stephanie, what's new in your world? STEPHANIE: So, a few weeks ago, we did an episode on spiking in response to a listener question. And I wanted to kind of revisit that topic for a little bit because I've been doing a lot of spiking on my client project. And for those who are not familiar, the way that I understand or define spikes is kind of as an exploration phase to figure out if a technical implementation might work. Or if you have a feature request with some unknowns, you can spend some time-boxed spiking to figure out what those unknowns might be. And I'm working on your typical legacy Rails application [laughs]. And I think one thing that we talked about last time was this idea of, at what point does spiking end up being just working on the feature [laughs]? And I think that's especially true in an older codebase, where you kind of have to go down a few rabbit holes, maybe, just to even find out if something will trip you up down the line. And the way I approached that this time around was just, like, identifying the constraints and putting a little flag there for myself. Like, these were rabbit holes that I could go down, but, you know, towards the initial beginning phase of doing the spiking, I decided not to. I just kind of bookmarked it for later. And once I had identified the main constraints, that was when I was like, okay, like, what kind of solutions can I come up with for these constraints? And that actually then helped me kind of decide which ones we're pursuing a little bit more to get, like, the information I needed to ultimately make a decision about whether this was worth doing, right? It kind of kept me...I'm thinking about, you know, when you are bowling with those safety guards [laughs], it keeps your ball from just rolling into the gutter. I think it helped with not going too deep into places that I may or may not be super fruitful while also, I think, giving me enough information to have a more realistic understanding of, like, what this work would entail. JOËL: Would you say that this approach that you're taking is inspired or maybe informed by the conversation we had on the episode? STEPHANIE: I was especially interested in avoiding the kind of binary of like, no, we can't do this because the system just, you know, isn't able to support it, or it's just too...it would be too much work. That was something I was really, like you said, kind of inspired by after that conversation because I wanted to avoid that trap a little bit. And I think another really helpful framing was the idea of, like, okay, what would need to be done in order to get us to a place where this could be possible? And that's why I think identifying those constraints was important because they're not constraints forever. Like, we could do something about them if we really wanted to, so kind of avoiding the, like, it's not possible, right? And saying like, "It could be. Here's all the things that we need to do in order to make it possible." But I think that helped shift the conversation, especially with stakeholders and stuff, to be a little bit more realistic and collaborative. So, Joël, what's new in your world? JOËL: So, I'm also on a new client project, and a thing that's been really interesting in this codebase is that they've been using the dry-rb suite of gems pretty heavily. And I've seen a lot about the suite of gems. I've read about them. Interestingly, this is kind of the first time that I've been on a codebase that sort of uses them as a main pattern in the app. So, there's been a bit of a learning curve there, and it's been really interesting. STEPHANIE: This is exciting to me because I know you have a lot of functional programming background, also, so it's kind of surprising that you're only now, you know, using something that explicit from functional languages in Ruby. And I'm curious: what's the learning curve, if not the paradigm? Like, what are you kind of encountering? JOËL: I think there's a little bit of just the translation. How do these gems sort of approach this? So, they have to do a couple of, like, clever Ruby things to make some of these features work. Some of these also will have different method names, so a lot of just familiarizing myself with the libraries. Like, oh, well, this thing that I'm used to having called a particular thing has a slightly different name here or maybe not having all of the utilities. I was like, oh, how do we traverse with this particular library? Then you have to, like, look it up. So, it's a lot of like, how do I do this thing I know how to do in, let's say, Elm? How do I translate that into Ruby? But then, also, some of the interplay of how that works in code that also does some very kind of imperative side effecty things also written by a team that is getting used to the pattern. And so, you'll sort of see things where people are pulling things in, but maybe you don't fully understand the deeper underlying approach that's meant to be used. STEPHANIE: Have you noticed any use cases where the dry-rb patterns really shine in your application? JOËL: A thing that's nice is that I think it really forces you to think about your edge cases in a way that sometimes Ruby developers play very fast and loose with "Yeah, whatever, it will never be nil." Push to production immediately start getting NoMethodError in your bug tracker. I never do this, by the way, but you know. STEPHANIE: [laughs]. JOËL: Speaking from a friend's experience [laughs]. STEPHANIE: Asking for a friend, yeah [laughs]. JOËL: I think a thing that I've sort of had to figure out sort of every time I deal with these patterns in different languages is just the importance of good composition and good separation. Because you're adding these sort of wrapper context around things, if you're constantly wrapping and unwrapping, you're like, check things inside, and then do the next thing, and then unwrap again and branch and check and do the next thing, that code becomes really clunky in a way that you just sort of expect to do if you're just writing code in regular Ruby with a nil. But it doesn't really work with a dry-rb maybe or a result. So, the pattern that I have found that works really well is to extract sort of every operation that can be, let's say, that could fail so that it would give you a result back. Extract that out into its own separate function that will construct a success or a failure, and then have your sort of main code that wants to then do a bunch of these things together. All it does is use some of the dry-rb helper methods to compose all of these together, whether that's just some sort of, like, do notation, or binding, or fmap, or something like that, which allows you to have sort of individual chunks that can fail, and then one sort of aggregator piece of code that just finds a way to combine all of them nicely. And that avoids you having to do all this repetition. STEPHANIE: Yeah, that makes a lot of sense. JOËL: It's a pattern, I think; I had to learn the hard way when I was working with Elm. Because if you're taking a potential nullable value and then you want to do things with it but then that potential operation is also nullable because the input was potentially null, and then that just sort of propagates all the way down the chain. So, my whole chain of functions now is doing checks for nullability. And in Ruby, I could just be like, no, I checked it in the first function. I can then just trust that it's not null down the chain. Elm doesn't do the like, trust me, bro. The compiler will force you to validate every time, and then the code just blows up, and it gets really painful. So, I had to start thinking about new models of thinking that would separate out code that actually needs to care and code that doesn't need to care about nullability. And I wrote an article about that. That turned into actually a conference talk as well. And these sort of ideas have served me really well at Elm. And I think these translate pretty well to dry-rb as well. That's something that I'm exploring, but the principles seem like they're not tied to a particular language. STEPHANIE: Yeah, and it's kind of cool that you experienced all of that in working with Elm, where a compiler was there to yell at you [laughs] and kind of forcing you to...I don't know if do the right thing is the right word, but kind of think in the way that it wants you to think. And I can see people who are coming from Ruby and starting to experiment with dry-rb maybe needing a bit of that since it's not built-in in the tooling, just in a recoder view or just in conversations among devs. JOËL: [inaudible 09:26] Beyond just the idea of wrapping your values and making sure you check them all the time, there are patterns that make that easier or more painful. And even in something like Elm, the compiler would yell at me would make sure I could not have a runtime error by forgetting to check for nullability. It did not prevent me from writing monstrosities of nested repeated conditionals checking if nil, if nil, if nil. That I had to figure out some sort of, like, higher-level patterns that play nicely with that kind of software. And I think these are things that people have to sort of encounter, feel the pain, feel the frustration, and then move into those better patterns after the fact. And sometimes that's not easy because it's not obvious why that's a valuable pattern to approach. STEPHANIE: Yeah, I agree completely. Speaking of following patterns and kind of arriving at maybe an ideal version of [chuckles], you know, what you'd like your code to do, you know, to build what you are looking to build [laughs]...this is my very poor attempt at a smooth transition that Joël [laughter] manages to be able to do [laughs] whenever we're trying to shift into the topic of the episode. Anyway, today, we were hoping to talk a little bit about this idea between being an idealistic programmer and a pragmatic programmer and the different journeys that we've each been on in arriving kind of how to balance the two. JOËL: Yeah, you know, I think neither of these are absolutes, right? It's a spectrum. You probably move around that spectrum from day to day, and then probably, like, more general trends over your career. But I'm curious, for you today, if you had to pick one of those labels, like, which sort of zone of the spectrum would you put yourself in? Do you think you're more idealistic or more pragmatic? STEPHANIE: I think I'm in a more of an idealistic zone right now. JOËL: Would you say you're kind of like middle trending idealistic or kind of, like, pretty far down the idealistic side? STEPHANIE: Middle trending idealistic. I like that way of describing it. I want to know where you are. And then I kind of wanted to try to take a step back and even define what that means for both of us. JOËL: Right, right. I think the way I'd probably describe myself is a recovering idealist. STEPHANIE: Oof. Yeah [laughs]. JOËL: I think there was a time where I was really idealistic. I really like knowing sort of underlying theory of software construction, broader patterns. By patterns here, I don't mean necessarily, like, you know, the Gang of Four, but just general sort of approaches that work well and using that to guide my work. But I've also been trending a lot more into the, like, pragmatic side of things in the past few years. STEPHANIE: So, could you kind of tell me a little bit about what does pragmatic mean for you and what does ideal mean for you? JOËL: So, I think the pragmatic side of me it's about delivering working software. If you're not shipping anything, you know, the most beautiful piece of art that you've created just warms your heart is useless. So, I think I'm sort of at the extreme end of pragmatism, right? It's all about shipping and shipping fast. And, in the end, that's generally the goal of software. On the more idealistic side, the sort of doing everything kind of perfect or by the book, or, you know, maybe in a way that brings you personal satisfaction, oftentimes, at the expense of shipping and vice versa. Sometimes shipping comes at the expense of writing absolutely terrible code, but, of course, you know, there's value in both. Shipping is what actually delivers value to your users, your company, yourself if you're using the software. But if you're not following patterns and things, you're often stuck in a really short-term thinking loop, where you are maybe delivering value today at the cost of being able to deliver value tomorrow or writing code that is unreadable or code that is difficult to collaborate on. So, more than just me shipping an individual feature, I've got to think about, while I'm working with a team, how can I help them be able to ship features or build on top of my work for tomorrow? So, that's sort of how I visualize the field. I'm curious what the words idealism and pragmatism mean to you. STEPHANIE: Yeah. I agree with you that pragmatism is, you know, this idea of delivering working software. And I think I have seen it very, you know, kind of condensed as, like, moving quickly, getting stuff out the door, basically, like, end result being, like, a thing that you can use, right? I think I've personally been reassessing that idea a lot because I'm kind of almost wondering like, well, what are we moving quickly for [laughs]? I sometimes have seen pragmatism just end there being like, okay, like, it's all about velocity. And then, I'm kind of stuck being like, well, if you write working software for, you know, completely the wrong thing, is that still pragmatic? I don't know. So, that's kind of where I'm at these days with–I'm feeling a little bit more suspect of pragmatism, at least wanting to make sure that, especially with the people that I'm working with day to day, that we're agreeing on what that means and what success means. And then, as for idealism, I think also, actually, I now have a little bit of duality in terms of how I understand that as well. One of them being, yes, definitely, like, by the book or, like, by the ideas that we've, you know, some very smart people [laughs] have figured out as, like, this is clean or good quality, or these are the patterns to, you know, make your code as, again, as clean, I don't know, kind of putting air quotes around that, as possible. And then, I actually like what you really said about code that warms your heart [laughs] that you feel, like, really moved by or, like, just excited about or inspired by because I think that can also be a little bit different from just following theories that other people have defined. The more I spend doing this stuff, the more I am convinced that writing software is actually a very creative practice. And that's something that I've, like, definitely had to balance with the pragmatism a bit more because there are days when it's just not coming [chuckles], you know, like, I just stare at a blank, new file. And I'm like, I can't even imagine what these classes would be because, like, that creative part of my brain just, like, isn't on that day. So, that's kind of where I'm sitting in terms of, like, what idealistic programming kind of seems to me. JOËL: There's definitely an element of programming that feels like self-expression, you know, there are parameters around that. And working with a team, you probably all sort of, like, move towards some average. But I would definitely say that there is some element of self-expression in coding. STEPHANIE: Yeah, 100%. Have you heard about this paper called Programming as Theory Building? JOËL: The name sounds vaguely familiar, but I can't place the main idea in my mind right now. STEPHANIE: It's, like, an academic-ish paper from the 80s. And I'll link to it in the show notes because I can't remember the author right now. But the idea is writing code is actually just one way of expressing a theory that we are building. In fact, that expression doesn't even....it's like, it's impossible for it to fully encapsulate everything that was involved in the building of the theory because every decision you make, you know, you decide what not to do as well, right? Like, all the things that you didn't encode in your application is still part of this theory, like stuff that you rejected in order to interpret and make abstract the things that you are translating from the quote, unquote "real world" into code. That really stuck with me because, in that sense, I love this idea that you can create your own little world, right? Like, you're developing it when you code. And that is something that gets lost a little bit when we're just focused on the pragmatic side of things. JOËL: Where things get tricky as well is that when you're working with a team, you're not just building your own little world. You're building a shared world with shared mental models, shared metaphors. That's where oftentimes it becomes important to make sure that the things that you are thinking about are expressed in a way that other people could read your code and then immediately pick up on what's happening. And that can be through things like documentation, code comments. It can also be through more rigorous data modeling. So, for example, I am a huge fan of value objects in general. I tend to not have raw numbers floating around in an app. I like to wrap them in some kind of class and say, "Hey, these numbers that are floating around they actually represent a thing," and I'll name that thing so that other people can get a sense that, oh, it is one of the moving parts of this app, and then here are the behaviors that we expect on it. And that is partly for sort of code correctness and things like that but also as a sort of way of communicating and a way of contributing to that shared reality that we're creating with the team in a way that if I just left a raw number, that would be almost, like, leaving something slightly undefined. Like, the number is there. It does a thing, but what it does is maybe a little bit more implied. I know in my mind that this is a dollar amount, and maybe there's even a comment above it that says, "Dollar amount." But it makes it a little bit harder for it to play in with everybody else's realities or view of the system than if it were its own object. STEPHANIE: Yeah, I like what you said about you're building a shared world with your fellow colleagues. And that helped explain to me why, as some people say, naming is the hardest part about building software because, yeah, like you said, even just saying you are wanting to make a method or class expressive. And we talked about how code is a way of expressing yourself. You could, like, name all your stuff in Wingdings [laughs], but we don't. I actually don't know if you could do that. But that was, for some reason, what I imagined. I was like, it's possible, and you could deliver software in complete gibberish [laughs]. JOËL: In theory, could you say that naming your variables as emoji is the most expressive way? Because now it's all emotions. STEPHANIE: A picture is worth a thousand words, as they say. JOËL: So, this variable is the frowny face, upside-down smile face. It doesn't get more expressive than that. STEPHANIE: At a former company, in our Slack workspace, I had a co-worker who loved to use the circus tent emoji to react to things. And, like, I'm convinced that no one really knew what it meant, but we also kind of knew what it meant. We were just like, oh yeah, that's the emoji that she uses to express amusement or, like, something a little bit ironic. And we all kind of figured it out [laughs] eventually. So, again, I do think it's possible. I bet someone has done, like, a creative experiment with writing an application in just emojis. This is now going to be some research I do after this episode [laughter]. JOËL: It is fun when you have, like, a teammate. You know they have the signature emoji that they respond to on things. STEPHANIE: Yep. Absolutely. So, you know, we kind of spent a little bit of time talking about idealism. I actually wanted to pull back to the idea of pragmatism because, in preparation for this episode, I also revisited my copy of The Pragmatic Programmer. Are you familiar with this book? Have you read it at all? JOËL: I have read it. It's been probably ten years. We did, I think, a book club at thoughtbot to go through the book. STEPHANIE: I was skimming the table of contents because I was curious about, again, that, like, definition of pragmatism. You and I had kind of talked about how it can be short-sighted. But what I was actually pretty impressed with, and I imagine this is why the book holds up, you know, after decades, is success for them also means being able to continue to deliver quality software. And that idea of continuity kind of implied, to me, that there was an aspect of, like, making sure the quality meets a certain threshold and, like, incorporating these theories and doing the best practices because they're thinking about success over time, right? Not just the success of this particular piece that you're delivering. JOËL: I would say most people in our industry are sort of balancing those two objectives, right? They're like, we want to have a decent velocity and ship things, but at the same time, we want to be able to keep delivering. We want a certain threshold of quality. In between those two objectives, there is a sea of trade-offs, and how you manage them are probably a little bit part of your personality as a developer and is probably also, to a certain extent, a function of your experience, learning sort of when to lean more into taking some shortcuts to ship faster and when to double down on certain practices that increase code quality, and what aspects of quality value more than others because not all forms of quote, unquote, "quality" are the same. I think a sort of source of danger, especially for newer developers, is you sort of start on almost, like, a hyper-pragmatic side of things because most people get into software because they want to build things. And the ultimate way to build is to ship, and then you sort of encounter problems where you realize, oh, this code is really clunky. It's harder and harder to ship. Let me learn some elements of code quality. Let's get better at my craft so that I can build software that has fewer bugs or that I can ship more consistently. And that's great. And then, you sort of run into some, like, broader sort of theories of programming: patterns, structures, things like that. And it becomes very easy to sort of blindly copy-paste that everywhere to the point where I think it's almost a bit of a meme, the, like, intermediate programmer who's read Clean Code or the Design Patterns book and is just now, like, applying these things blindly to every piece of code they encounter to the annoyance of the entire team. STEPHANIE: I think you just about described my trajectory [laughter], though hopefully, I was not so obnoxious about [laughs] it for my team having to deal with my, like, discovering [laughs] theories that have long been used. JOËL: I think we kind of all go through that journey to a certain extent, right? It's a little bit different for every one of us, but I think this is a journey that is really common for developers. STEPHANIE: Yeah. One thing I frequently think about a lot is how much I wished I had known some of that theory earlier. But I don't think I have an answer one way or another. It's like; I'm not sure if having that knowledge earlier really would have helped me because I've also definitely been in...I'm just thinking about, like, when I was in college in lectures trying to absorb theories that made no sense to me because I had no, like, practical experience to connect it to. It's almost, like, maybe there is, like, that perfect time [laughs] where it is the most valuable for what you're doing. And I don't know. I kind of believe that there is a way to bridge that gap. JOËL: I mean, now we're kind of getting into an element of pedagogy. Do you sort of teach the theory first, and then show how to apply it to problems? Or do you show problems and then introduce bits of theory to help people get unstuck and maybe then cap it off by like, oh, these, like, five different, like, techniques I showed you to, like, solve five different problems, turns out they all fit in some grand unified theory? And, like, here's how the five things you thought were five different techniques are actually the same technique viewed from five different perspectives. Let me blow your mind. STEPHANIE: That's a Joël approach [laughter] to teaching if I've ever heard one. JOËL: I'm a huge fan of that approach. Going back to some of the, like, the functional programming ideas, I think that's one that really connected for me. I struggled to learn things like monads, and functors, and things like that. And I think, in my mind, these two approaches is like the Haskell school of teaching and the Elm school of teaching. Haskell will sort of say, "Hey, let me teach you about this theory of monads and all these things, and then, we'll look at some ways where that can be applied practically." Whereas Elm will say, "No, you don't need to know about this. Let's look at some practical problems. Oh, you've got null values you need to check. Here's how you can, like, handle nullability in a safe way. Oh, you've got a bunch of HTTP requests that might resolve in random order, and you want to, like, deal with them when they all come back. Here's some tips on how you can do that." And then, you have three or four things, and then, eventually, it just sort of lets you say, "Wait a minute, all of these problems are sort of all the same, and it turns out they all fit in some unified theory." And then, the light bulb goes off, and you're like, "Ooh, so now when I'm dealing with unknown blobs of Jason trying to parse data out of them, I'll bet I can use the same techniques I used for chaining HTTP requests to dig multiple dependent pieces of JSON." STEPHANIE: Yeah. And that's so satisfying, right? It really is kind of leveling up in that Galaxy Brain meme sort of way. JOËL: Yeah. And that's maybe to a certain extent even a value of idealism because if you build your system in such a way that it follows some of these patterns, then insights and intuitions that people have in one part of your code can then carry to other parts of your code, and that's incredibly powerful. STEPHANIE: Yeah. And I almost wonder because you also mentioned kind of where you end up on the spectrum is a function of your experience. I wonder if us, you know, being consultants and seeing patterns across many applications also kind of contributes to the striving for idealism [laughs]. JOËL: It's kind of both, right? Because there's very high incentive to ship pretty rapidly, especially if you're on a shorter engagement or if you're on a project that has a shorter timescale. But also, yes, because you've seen so many projects, you've seen how things can go wrong. Also, you've seen the same problem from 20 different perspectives that are all slightly different. And so, some of those broader patterns can start emerging in your head. STEPHANIE: Yeah, honestly, I think that's kind of the work that I enjoy the most in consulting because a lot of clients bring us on when they're like, "Hey, like, we've reached a point where our velocity has slowed down. Like, can you help us unstick our developers?" And that's actually when I've found that leaning on the theories and maybe a little bit of idealism is actually really useful because I'm kind of providing those tools to developers at this time when they need it. That's kind of why I have been saying trending idealism because I have found that particularly useful at work. JOËL: There's an element here of, like, looking at a bunch of different use cases and then finding some sort of unifying model or theory. And that's a word that I think programmers have a love-hate relationship with: Abstraction. I don't know about you, but designing abstractions is a lot of fun for me. I love designing abstractions. I have always loved designing abstractions. It's not always the best use of my time, and it's not always the best thing for a codebase. STEPHANIE: Ooh, okay, okay. This was a good transition. I hear you that, like, yeah, love-hate relationship. It's hard. That's kind of where I've ended up. It's really hard. And I think it's because it requires that creative thinking. JOËL: It requires that creative thinking. And then also, like, it requires you to sort of see more broadly, a more broad picture. What are the things that are connected, the things that are disconnected, even though they seem related? And, like, being able to sort of slice those similarities from each other. STEPHANIE: Yeah. I agree. And the interesting part is that, like, a lot of the time you just don't know yet. And you kind of have to come back to reality and admit that you don't know yet, you know, got to come back to earth, take a look around, and, yeah, you can go through the thought exercise of thinking [laughs] about all of the possibilities, and I imagine you could do that forever [laughs]. JOËL: I mean, that's why we have heuristics like the rule of three that says, "Don't abstract something out or attempt to DRY code until you've seen three use cases of it." So, maybe leave a little bit of duplication or a little bit of maybe not perfectly factored code until you have a couple of more examples. And the sort of real picture starts emerging a little bit more. STEPHANIE: So, I think we are kind of at this topic already, but was there a moment or was there something that kind of helped you realize, like, oh, I can't be in that space of imagining abstractions [laughs] forever when I have to deliver software? Like, what changed for you to be the, as you said yourself, recovering idealist and having to maybe employ some more pragmatic heuristics? JOËL: And I think, for me, it's partly being a consultant and being in a lot of projects and having that pressure to work with deadlines and sort of not having an infinite canvas to paint with, having to sort of fit some of my grand ideas into the reality of, we've got a week or two weeks to get this thing done, and also working with a team, and some ideas don't work well with every team. Every team is kind of at a different place. And abstractions sort of only serve you as well as they are useful to not only you but the team at large. So, if a team is not comfortable with a set of abstractions, or it's sort of, like, too far down a path, then that can be really challenging. And that's where something like the dry-rb set of gems, which has some really fun abstractions like a mental model for doing things, depending on the team, that can be a really heavy lift. And so, as much as I like those patterns, I might think long and hard before I try to push this on a whole team. STEPHANIE: Yeah, I kind of had to navigate a situation like that recently, where I was doing a code review, and I had left some suggestions about refactoring to encapsulate some responsibilities better. And then, I was like, oh, and then I noticed another thing that we could do to make that easier. And it, you know, definitely can start to spiral. And the author, you know, kind of responded to me and said, "Hey, like, I really appreciate these comments, but we are a bit tight on deadline for this project. So, is it okay if I, like, revisit this when we've delivered it?" And, you know, I was just like, "Yeah, it's totally up to you." At the end of the day, I want whoever's authoring this code to have, like, full agency about how they want to move forward. And it was really helpful for me to get that context of, like, oh, they're a bit tight on the deadline because then I can start to meet them where they're at. And maybe I can give some suggestions for moving towards that ideal state, but ones that are lower left, and that is still better than nothing. JOËL: That sounds awfully pragmatic. STEPHANIE: [laughs] JOËL: Moving in a positive direction, we're getting halfway. It's better than nothing. That's very pragmatic. STEPHANIE: Hmm. Wow. But it's pragmatically moving towards idealism. JOËL: [laughs] STEPHANIE: If that is even possible [laughs]. JOËL: Uh-huh. STEPHANIE: That's maybe the book that I'm going to write, not The Pragmatic Programmer, but The Pragmatically Idealistic Programmer [laughs]. JOËL: The Pragmatic Idealist. STEPHANIE: Ooh, yeah, I like that. Okay. Watch out for that book coming 2030 [laughter], written by me and Joël. JOËL: So, I think you brought up a really interesting point, which is the idea of pragmatism versus idealism when it comes to code review. Do you find that you think about these ideas differently when reviewing somebody else's code versus when you write your own? STEPHANIE: Oooh, yeah. I'm not sure exactly why, but definitely, when I'm reviewing someone else's code, I'm already in the headspace of, you know, I have some separation, right? Like, I'm not in the mode of thinking very hard [laughs] about what I'm creating. I'm just, like, in the editing kind of phase. And then, I can actually pull more from different theories and ideas, and I find that actually quite easier. When I'm writing my own code, it's just whatever comes out, right? And then, hopefully, I have the time to revisit it and give it a scan, and then start to integrate the, like, idealistic theories and the patterns that I would like to be using. But it definitely...for patterns that I feel a lot more confident about or more familiar with, they just come out mostly kind of oriented in that way if I have the time, or sometimes I will make the time, you know. I'll just say, "It's not done yet," because I know it can be better. I think that could be another, like, pragmatically idealist way of handling that. JOËL: [laughs] STEPHANIE: Right? It's just telling people, "I'm not done." [laughs] It's not done until I do at least give it an attempt. JOËL: So, it's kind of a two-phase thing when you're writing your own code, whereas it's only a single phase when you're reviewing somebody else's. STEPHANIE: Yeah. Yeah. But, like I said earlier, it's like, I also really believe that I don't want to impose any of my ideas [laughs] onto others. I really believe that people have to arrive at it on their own. So, it used to bother me a little bit more when I was just like, oh, but this way is better [laughs]. When people wouldn't get on board, I would be sad about it. But as long as I know that I, like, left that comment, then I can give myself a pat on the back for trying to move towards that ideal state. What about you [laughs]? JOËL: I think this is probably also where I'm, like, now a recovering idealist. There was a time where I would leave a ton of comments on someone's PR. I almost had a view of like, how can I help you get your PR to be the best it can possibly be? And sometimes, if you start with something that's very rough around the edges, you're leaving a lot of comments. And I've been that guy who's left 50 comments on a PR. In retrospect, I think that was not being a good teammate. STEPHANIE: Hmm. JOËL: So, I think maybe my mental model or my, like, goal for PR review has changed a little bit. It's less about how can I help you make your code the best it can possibly be? And a how can I help you get your code to mergeable? And it's possible that mergeable means best that it can possibly be, but that's usually not the case. So, I'm going to give you some feedback: some things that confuse me, maybe raise one or two patterns that are existing in the app that maybe you weren't aware of that you should maybe consider applying. Maybe I'll raise a couple of ideas that are new, but that apply here. And those might just be a, "Hey, let's just think about this. Maybe we don't want to do this in this PR, but maybe we want to look at them at some point. Or we should be thinking about this in a sort of rule of three situation. If we see this come up another time, maybe consider introducing a strategy pattern here, or maybe consider making this a value object, or separating these side effects from these pure behavior." But it's more of a dialogue about how can I help you get your PR to the point where it is mergeable? STEPHANIE: Yeah. Another thing I thought about just now is both are meaningful or, like, both can provide meaning in different ways, and people ascribe different amounts of meaning to both; where I had worked with someone, a client developer before, who was not super interested in doing any kind of refactoring or, like, any, you know, second passes for quality. Because, for him, like, he just wanted to ship, right? That was where he found meaning in his work. Whereas that actually made my work feel a lot more meaningless [chuckles] because I'm like, well, if we're just kind of hands on a keyboard, like robots shipping code, I don't know, that doesn't feel particularly motivating for me. You know, I do want to employ some of that craft a little bit more. JOËL: And, I guess, yeah, idealism versus pragmatism is also...it's a personal individual thing. There's an element where it's a team decision, or at least a sense of, like, how much quality do we need at this point in the life cycle of the project? And what are the areas where we particularly want to emphasize quality? What are our quality standards? And that's, to a certain extent, consensus among the team that it's individual members. And it's also coming from team leadership. STEPHANIE: Yeah. Yeah, exactly. I mentioned that, you know, just to, I think, shed a little bit of light that it's usually not personal, right [laughs]? There's that part of understanding that is really important to, yeah, like, keep building this shared world of writing software, and, hopefully, it should be meaningful for all of us. JOËL: I think a few takeaways that I have would be, one, the value of, like, theory and idealism. These things help you to become a better developer. They help you to spot patterns. It's probably good to sort of have in the background always be learning some new thing, whether that's learning a new set of patterns, or learning some mental models, thinking about, oh, the difference between side effects and pure code, learning about particular ways of structuring code. These are all things that are good to have in your back pocket to be able to apply to the code that you're doing, even if it's a sort of after-the-fact, hey, I've done a similar task three different times. Is there a broader principle? But then, also, take the time to really make sure that you're focusing on shipping code, and maybe that's learning to work in smaller chunks, working iteratively, learning to scope your work well. Because, in the end, delivering value is a thing that is something that we could all probably benefit from doing more of. And then, finally, taking some time to self-reflect, a little bit of self-awareness in this area. What are the aspects of pragmatism and idealism that you find personally meaningful? What are the elements that you think bring value to your work, to your team? And let that sort of guide you on your next code writing or PR review. STEPHANIE: On that note, shall we wrap up? JOËL: Let's wrap up. STEPHANIE: Show notes for this episode can be found at bikeshed.fm. JOËL: This show has been produced and edited by Mandy Moore. STEPHANIE: If you enjoyed listening, one really easy way to support the show is to leave us a quick rating or even a review in iTunes. It really helps other folks find the show. JOËL: If you have any feedback for this or any of our other episodes, you can reach us @_bikeshed, or you can reach me @joelquen on Twitter. STEPHANIE: Or reach both of us at hosts@bikeshed.fm via email. JOËL: Thanks so much for listening to The Bike Shed, and we'll see you next week. ALL: Byeeeeeeee!!!!!! AD: Did you know thoughtbot has a referral program? If you introduce us to someone looking for a design or development partner, we will compensate you if they decide to work with us. More info on our website at: tbot.io/referral. Or you can email us at: referrals@thoughtbot.com with any questions.

    420: Test Database Woes

    Play Episode Listen Later Mar 26, 2024 28:16


    Joël shares his recent project challenge with Tailwind CSS, where classes weren't generating as expected due to the dynamic nature of Tailwind's CSS generation and pruning. Stephanie introduces a personal productivity tool, a "thinking cap," to signal her thought process during meetings, which also serves as a physical boundary to separate work from personal life. The conversation shifts to testing methodologies within Rails applications, leading to an exploration of testing philosophies, including developers' assumptions about database cleanliness and their impact on writing tests. Avdi's classic post on how to use database cleaner (https://avdi.codes/configuring-database_cleaner-with-rails-rspec-capybara-and-selenium/) RSpec change matcher (https://rubydoc.info/gems/rspec-expectations/RSpec%2FMatchers:change) Command/Query separation (https://martinfowler.com/bliki/CommandQuerySeparation.html) When not to use factories (https://thoughtbot.com/blog/speed-up-tests-by-selectively-avoiding-factory-bot) Why Factories? (https://thoughtbot.com/blog/why-factories) Transcript:  STEPHANIE: Hello and welcome to another episode of The Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I'm Stephanie Minn. JOËL: And I'm Joël Quenneville. And together, we're here to share a bit of what we've learned along the way. STEPHANIE: So, Joël, what's new in your world? JOËL: I'm working on a new project, and this is a project that uses Tailwind CSS for its styling. And I ran into a bit of an annoying problem with it just getting started, where I was making changes and adding classes. And they were not changing the things I thought they would change in the UI. And so, I looked up the class in the documentation, and then I realized, oh, we're on an older version of the Tailwind Rails gem. So, maybe we're using...like, I'm looking at the most recent docs for Tailwind, but it's not relevant for the version I'm using. Turned out that was not the problem. Then I decided to use the Web Inspector and actually look at the element in my browser to see is it being overwritten somehow by something else? And the class is there in the element, but when I look at the CSS panel, it does not show up there at all or having any effects. And that got me scratching my head. And then, eventually, I figured it out, and it's a bit of a facepalm moment [laughs]. STEPHANIE: Oh, okay. JOËL: Because Tailwind has to, effectively, generate all of these, and it will sort of generate and prune the things you don't need and all of that. They're not all, like, statically present. And so, if I was using a class that no one else in the app had used yet, it hadn't gotten generated. And so, it's just not there. There's a class on the element, but there's no CSS definition tied to it, so the class does nothing. What you need to do is there's a rake task or some sort of task that you can run that will generate things. There's also, I believe, a watcher that you can run, some sort of, like, server that will auto-generate these for you in dev mode. I did not have that set up. So, I was not seeing that new class have any effect. Once I ran the task to generate things, sure enough, it worked. And Tailwind works exactly how the docs say they do. But that was a couple of hours of my life that I'm not getting back. STEPHANIE: Yeah, that's rough. Sorry to hear. I've also definitely gone down that route of like, oh, it's not in the docs. The docs are wrong. Like, do they even know what they're talking about? I'm going to fix this for everyone. And similarly have been humbled by a facepalm solution when I'm like, oh, did I yarn [laughs]? No, I didn't [laughs]. JOËL: Uh-huh. I'm curious, for you, when you have sort of moments where it's like the library is not behaving the way you think it is, is your default to blame yourself, or is it to blame the library? STEPHANIE: [laughs]. Oh, good question. JOËL: And the follow-up to that is, are you generally correct? STEPHANIE: Yeah. Yep, yep, yep. Hmm, I will say I externalize the blame, but I will try to at least do, like, the basic troubleshooting steps of restarting my server [laughter], and then if...that's as far as I'll go. And then, I'll be like, oh, like, something must be wrong, you know, with this library, and I turn to Google. And if I'm not finding any fruitful results, again, you know, one path could be, oh, maybe I'm not Googling correctly, but the other path could be, maybe I've discovered something that no one else has before. But to your follow-up question, I'm almost, like, always wrong [laughter]. I'm still waiting for the day when I, like, discover something that is an actual real problem, and I can go and open an issue [chuckles] and, hopefully, be validated by the library author. JOËL: I think part of what I heard is that your debugging strategy is basic, but it's not as basic as Joël's because you remember to restart the server [chuckles]. STEPHANIE: We all have our days [laughter]. JOËL: Next time. So, Stephanie, what is new in your world? STEPHANIE: I'm very excited to share this with you. And I recognize that this is an audio medium, so I will also describe the thing I'm about to show you [laughs]. JOËL: Oh, this is an object. STEPHANIE: It is an object. I got a hat [laughs]. JOËL: Okay. STEPHANIE: I'm going to put it on now. It's a cap that says "Thinking" on it [laughs] in, like, you know, fun sans serif font with a little bit of edge because the thinking is kind of slanted. So, it is designy, if you will. It's my thinking cap. And I've been wearing it at work all week, and I love it. As a person who, in meetings and, you know, when I talk to people, I have to process before I respond a lot of the time, but that has been interpreted as, you know, maybe me not having anything to say or, you know, people aren't sure if I'm, you know, still thinking or if it's time to move on. And sometimes I [chuckles], you know, take a long time. My brain is just spinning. I think another funny hat design would be, like, the beach ball, macOS beach ball. JOËL: That would be hilarious. STEPHANIE: Yeah. Maybe I need to, like, stitch that on the back of this thinking cap. Anyway, I've been wearing it at work in meetings. And then, when I'm just silently processing, I'll just point to my hat and signal to everyone what's [laughs] going on. And it's also been really great for the end of my work day because then I take off the hat, and because I've taken it off, that's, like, my signal, you know, I have this physical totem that, like, now I'm done thinking about work, and that has been working. JOËL: Oh, I love that. STEPHANIE: Yeah, that's been working surprisingly well to kind of create a bit more of a boundary to separate work thoughts and life thoughts. JOËL: Because you are working from home and so that boundary between professional life and personal life can get a little bit blurry. STEPHANIE: Yeah. I will say I take it off and throw it on the floor kind of dramatically [laughter] at the end of my work day. So, that's what's new. It had a positive impact on my work-life balance. And yeah, if anyone else has the problem of people being confused about whether you're still thinking or not, recommend looking into a physical thinking cap. JOËL: So, you are speaking at RailsConf this spring in Detroit. Do you plan to bring the thinking cap to the conference? STEPHANIE: Oh yeah, absolutely. That's a great idea. If anyone else is going to RailsConf, find me in my thinking cap [laughs]. JOËL: So, this is how people can recognize Bikeshed co-host Stephanie Minn. See someone walking around with a thinking cap. STEPHANIE: Ooh. thinkingbot? JOËL: Ooh. STEPHANIE: Have I just designed new thoughtbot swag [laughter]? We'll see if this catches on. JOËL: So, we were talking recently, and you'd mentioned that you were facing some really interesting dilemmas when it came to writing tests and particularly how tests interact with your test database. STEPHANIE: Yeah. So, I recently, a few weeks ago, joined a new client project and, you know, one of the first things that I do is start to run those tests [laughs] in their codebase to get a sense of what's what. And I noticed that they were taking quite a long time to get set up before I even saw any progress in terms of successes or failures. So, I was kind of curious what was going on before the examples were even run. And when I tailed the logs for the tests, I noticed that every time that you were running the test suite, it would truncate all of the tables in the test database. And that was a surprise to me because that's not a thing that I had really seen before. And so, basically, what happens is all of the data in the test database gets deleted using this truncation strategy. And this is one way of ensuring a clean slate when you run your tests. JOËL: Was this happening once at the beginning of the test suite or before every test? STEPHANIE: It was good that it was only running once before the test suite, but since, you know, in my local development, I'm running, like, a file at a time or sometimes even just targeting a specific line, this would happen on every run in that situation and was just adding a little bit of extra time to that feedback loop in terms of just making sure your code was working if that's part of your workflow. JOËL: Do you know what version of Rails this project was in? Because I know this was popular in some older versions of Rails as a strategy. STEPHANIE: Yeah. So, it is Rails 7 now, recently upgraded to Rails 7. It was on Rails 6 for a little while. JOËL: Very nice. I want to say that truncation is generally not necessary as of Rails...I forget if it's 5 or 6. But back in the day, specifically for what are now called system tests, the sort of, like, Capybara UI-driven browser tests, you had, effectively, like, two threads that were trying to access the database. And so, you couldn't have your test data wrapped in a transaction the way you would for unit tests because then the UI thread would not have access to the data that had been created in a transaction just for the test thread. And so, people would use tools like Database Cleaner to use a truncation strategy to clear out everything between tests to allow a sort of clean slate for these UI-driven feature specs. And then, I want to say it's Rails 5, it may have been Rails 6 when system tests were added. And one of the big things there was that they now could, like, share data in a transaction instead of having to do two separate threads and one didn't have access to it. And all of a sudden, now you could go back to transactional fixtures the way that you could with unit tests and really take advantage of something that's really nice and built into Rails. STEPHANIE: That's cool. I didn't know that about system tests and that kind of shift happening. I do think that, in this case, it was one of those situations where, in the past, the database truncation, in this case, particular using the Database Cleaner gem was necessary, and that just never got reassessed as the years went by. JOËL: That's one of the classic things, right? When you upgrade a Rails app over multiple versions, and sometimes you sort of get a new feature that comes in for free with the new version, and you might not be aware of it. And some of the patterns in the app just kind of keep going. And you don't realize, hey, this part of the app could actually be modernized. STEPHANIE: So, another interesting thing about this testing situation is that I learned that, you know, if you ran these tests, you would experience this truncation strategy. But the engineering team had also kind of played around with having a different test setup that didn't clean the database at all unless you opted into it. JOËL: So, your test database would just...each test would just keep writing to the database, but they're not wrapped in transactions. Or they are wrapped in transactions, but you may or may not have some additional data. STEPHANIE: The latter. So, I think they were also using the transaction strategy there. But, you know, there are some reasons that you would still have some data persisted across test runs. I had actually learned that the use transactional fixtures config for RSpec doesn't roll back any data that might have been created in a before context hook. JOËL: Yep, or a before all. Yeah, the transaction wraps the actual example, but not anything that happens outside of it. STEPHANIE: Yeah, I thought that was an interesting little gotcha. So, you know, now we had these, like, two different ways to run tests. And I was chatting with a client developer about how that came to be. And we then got into an interesting conversation about, like, whether or not we each expect a clean database in the first place when we write our tests or when we run our tests, and that was an area that we disagreed. And that was cool because I had not really, like, thought about like, oh, how did I even arrive at this assumption that my database would always be clean? I think it was just, you know, from experience having only worked in Rails apps of a certain age that really got onto the Database [laughs] Cleaner train. But it was interesting because I think that is a really big assumption to make that shapes how you then approach writing tests. JOËL: And there's kind of a couple of variations on that. I think the sort of base camp approach of writing Rails with fixtures, you just sort of have, for the most part, an existing set of data that's there that you maybe layer on a few extra things on. But there's base level; you just expect a bunch of data to exist in your test database. So, it's almost going off the opposite assumption, where you can always assume that certain things are already there. Then there's the other extreme of, like, you always assume that it's empty. And it sounds like maybe there's a position in the middle of, like, you never know. There may be something. There may not be something, you know, spin the wheel. STEPHANIE: Yeah. I guess I was surprised that it, you know, that was just a question that I never really asked myself prior to this conversation, but it could feel like different testing philosophies. But yeah, I was very interested in this, you know, kind of opinion that was a little bit different from mine about if you assume that your database, your test database, is not clean, that kind of perhaps nudges you in the direction of writing tests that are less coupled to the database if they don't need to be. JOËL: What does coupling to the database mean in this situation? STEPHANIE: So, I'm thinking about Rails tests that might be asserting on a change in database behavior, so the change matcher in RSpec is one that I see maybe sometimes used when it doesn't need to be used. And we're expecting, like, account to have changed the count of the number of records on it for a model have changed after doing some work, right? JOËL: And the change matcher from RSpec is one that allows you to not care whether there are existing records or not. It sort of insulates you from that. STEPHANIE: That's true. Though I guess I was thinking almost like, what if there was some return value to assert on instead? And would that kind of help you separate some side effects from methods that might be doing too much? And kind of when I start to see tests that have both or are asserting on something being returned, and then also something happening, that's one way of, like, figuring out what kind of coupling is going on inside this test. JOËL: It's the classic command-query separation principle from object-oriented design. STEPHANIE: I think another one that came to mind, another example, especially when you're talking about system tests, is when you might be using Capybara and you end up...maybe you're going through a flow that creates a record. But from the user perspective, they don't actually know what's going on at the database level. But you could assert that something was created, right? But it might be more realistic at that level of abstraction to be asserting some kind of visual element that had happened as a result of the flow that you're testing. JOËL: Yeah. I would, in fact, go so far as to say that asserting on the state of your database in a system test is an anti-pattern. System tests are sort of, by design, meant to be all about user behavior trying to mimic the experience of a user. And a user of a website is not going to be able to...you hope they're not able to SSH into [chuckles] your database and check the records that have been created. If they can, you've got another problem. STEPHANIE: I wonder if you could take this idea to the extreme, though. And do you think there is a world where you don't really test database-level concerns at all if you kind of believe this idea that it doesn't really matter what the state of it should be? JOËL: I guess there's a few different things on, like, what it matters about the state of it because you are asserting on its state sort of indirectly in a sort of higher level integration test. You're asserting that you see certain things show up on the screen in a system test. And maybe you want to say, "I do certain tasks, and then I expect to see three items in an unordered list." Those three items probably come from the database, although, you know, you could have it where they come from an API or something like that. So, the database is an implementation level. But if you had random data in your database, you might, in some tests, have four items in the list, some tests have five. And that's just going to be a flaky test, and that's going to be incredibly painful. So, while you're not asserting on the database, having control over it during sort of test setup, I think, does impact the way you assert. STEPHANIE: Yeah, that makes sense. I was suddenly just thinking about, like, how that exercise can actually tell you perhaps, like, when it is important to, in your test setup, be persisting real records as opposed to how much you can get away with, like, not interacting with it because, like, you aren't testing at that integration level. JOËL: That brings up a good point because a lot of tests probably you might need models, but you might not need persisted models to interact with them, if you're testing a method on a model that just does things based off its internal state and not any of the ActiveRecord database queries, or if you have some other service or something that consumes a model that doesn't necessarily need to query. There's a classic blog post on the thoughtbot blog about when you should not reuse. There's a classic blog post on the thoughtbot blog about when not to use FactoryBot. And, you know, we are the makers of FactoryBot. It helps set up records in your database for testing. And people love to use it all the time. And we wrote an article about why, in many cases, you don't need to create something into the database. All you need is just something in memory, and that's going to be much faster than using FactoryBot because talking to the database is expensive. STEPHANIE: Yeah, and I think we can see that in the shift from even, like, fixtures to factories as well, where test data was only persisted as needed and as needed in individual tests, rather than seeding it and having all of those records your entire test run. And it's cool to see that continuing, you know, that idea further of like, okay, now we have this new, popular tool that reduce some of that. But also, in most cases, we still don't need...it's still too much. JOËL: And from a performance perspective, it's a bit of a see-saw in that fixtures are a lot faster because they get inserted once at the beginning of your test run. So, a SQL execution at the beginning of a test run and then every test after that is just doing its thing: maybe creating a record inside of a transaction, maybe not creating any records at all. And so, it can be a lot faster as opposed to using FactoryBot where you're creating records one at a time. Every create call in a test is a round trip to the database, and those are expensive. So, FactoryBot tests tend to be more expensive than those that rely on fixtures. But you have the advantage of more control over what data is present and sort of more locality because you can see what has been created at the test level. But then, if you decide, hey, this is a test where I can just create records in memory, that's probably the best of all worlds in that you don't need anything created ahead with fixtures. You also don't need anything to be inserted using FactoryBot because you don't even need the database for this test. STEPHANIE: I'm curious, is that the assumption that you start with, that you don't need a persisted object when you're writing a basic unit test? JOËL: I think I will as much as possible try not to need to persist and only if necessary use persist records. There are strategies with FactoryBot that will allow you to also, like, build stubbed or just build in memory. So, there's a few different variations that will, like, partially do things for you. But oftentimes, you can just new up an object, and that's what I will often start with. In many cases, I will already know what I'm trying to do. And so, I might not go through the steps of, oh, new up an object. Oh no, I'm getting a I can't do the thing I need to do. Now, I need to write to the database. So, if I'm testing, let's say, an ActiveRecord scope that's filtering down a series of records, I know that's a wrapper around a database query. I'm not going to start by newing up some records and then sort of accidentally discovering, oh yeah, it does write to the database because that was pretty clear to me from the beginning. STEPHANIE: Yeah. Like, you have your mental shortcuts that you do. I guess I asked that question because I wonder if that is a good heuristic to share with maybe developers who are trying to figure out, like, should they create persisted records or, you know, use just regular instance in memory or, I don't know, even [laughs] use, like, a double [laughs]? JOËL: Yeah, I've done that quite a bit as well. I would say maybe my heuristic is, is the method under test going to need to talk to the database? And, you know, I may or may not know that upfront because if I'm test driving, I'm writing the test first. So, sometimes, maybe I don't know, and I'll start with something in memory and then realize, oh, you know, I do need to talk to the database for this. And this is for unit tests, in particular. For something more like an integration test or a system test that might require data in the database, system tests almost always do. You're not interacting with instances in memory when you're writing a system test, right? You're saying, "Given the database state is this when I visit this URL and do these things, this page reacts in such and such a way." So, system tests always write to the database to start with. So, maybe that's my heuristic there. But for unit tests, maybe think a little bit about does your method actually need to talk to the database? And maybe even almost give yourself a challenge. Can I get away with not talking to the database here? STEPHANIE: Yeah, I like that because I've certainly seen a lot of unit tests that are integration tests in disguise [laughs]. JOËL: Isn't that the truth? So, we kind of opened up this conversation with the idea of there are different ways to manage your database in terms of, do you clean or not clean before a test run? Where did you end up on this particular project? STEPHANIE: So, I ended up with a currently open PR to remove the need to truncate the database on each run of the test suite and just stick with the transaction for each example strategy. And I do think that this will work for us as long as we decide we don't want to introduce something like fixtures, even though that is actually also a discussion that's still in the works. But I'm hoping with this change, like, right now, I can help people start running faster tests [chuckles]. And should we ever introduce fixtures down the line, then we can revisit that. But it's one of those things that I think we've been living with this for too long [laughs]. And no one ever questioned, like, "Oh, why are we doing this?" Or, you know, maybe that was a need, however many years ago, that just got overlooked. And as a person new to the project, I saw it, and now I'm doing something about it [laughs]. JOËL: I love that new person energy on a project and like, "Hey, we've got this config thing. Did you know that we didn't need this as of Rails 6?" And they're like, "Oh, I didn't even realize that." And then you add that, and it just moves you into the future a little bit. So, if I understand the proposed change, then you're removing the truncation strategy, but you're still going to be in a situation where you have a clean database before each test because you're wrapping tests in transactions, which I think is the default Rails behavior. STEPHANIE: Yeah, that's where we're at right now. So, yeah, I'm not sure, like, how things came to be this way, but it seemed obvious to me that we were kind of doing this whole extra step that wasn't really necessary, at least at this point in time. Because, at least to my knowledge [laughs], there's no data being seeded in any other place. JOËL: It's interesting, right? When you have a situation where this was sort of a very popular practice for a long time, a lot of guides mentioned that. And so, even though Rails has made changes that mean that this is no longer necessary, there's still a long tail of apps that will still have this that may be upgraded later, and then didn't drop this, or maybe even new apps that got created but didn't quite realize that the guide they were following was outdated, or that a best practice that was in their head was also outdated. And so, you have a lot of apps that will still have these sort of, like, relics of the past. And you're like, "Oh yeah, that's how we used to do things." STEPHANIE: So yeah, thanks, Joël, for going on this journey with me in terms of, you know, reassessing my assumptions about test databases. I'm wondering, like, if this is common, how other people, you know, approach what they expect from the test database, whether it be totally clean or have, you know, any required data for common flows and use cases of your system. But it does seem that little in between of, like, maybe it is using transactions to reset for each example, but then there's also some persistence that's happening somewhere else that could be a little tricky to manage. JOËL: On that note, shall we wrap up? STEPHANIE: Let's wrap up. Show notes for this episode can be found at bikeshed.fm. JOËL: This show has been produced and edited by Mandy Moore. STEPHANIE: If you enjoyed listening, one really easy way to support the show is to leave us a quick rating or even a review in iTunes. It really helps other folks find the show. JOËL: If you have any feedback for this or any of our other episodes, you can reach us @_bikeshed, or you can reach me @joelquen on Twitter. STEPHANIE: Or reach both of us at hosts@bikeshed.fm via email. JOËL: Thanks so much for listening to The Bike Shed, and we'll see you next week. ALL: Byeeeeeeeeeeee!!!!!! AD: Did you know thoughtbot has a referral program? If you introduce us to someone looking for a design or development partner, we will compensate you if they decide to work with us. More info on our website at: tbot.io/referral. Or you can email us at: referrals@thoughtbot.com with any questions.

    419: What's New in Your World? (Extended Edition)

    Play Episode Listen Later Mar 19, 2024 37:13


    Stephanie introduces her ideal setup for enjoying coffee on a bike ride. Joël describes his afternoon tea ritual. Exciting news from the hosts: both have been accepted to speak at RailsConf! Stephanie's presentation, titled "So, Writing Tests Feels Painful. What now?" aims to tackle the issues developers encounter with testing while offering actionable advice to ease these pains. Joël's session will focus on utilizing Turbo to create a Dungeons & Dragons character sheet, combining his passion for gaming with technical expertise. Their conversation shifts to artificial intelligence and its potential in code refactoring and other applications, such as enhancing the code review process and solving complex software development problems. Joël shares his venture into combinatorics, illustrating how this mathematical approach helped him efficiently refactor a database query by systematically exploring and testing all potential combinations of query segments. Transcript:  JOËL: Hello and welcome to another episode of The Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I'm Joël Quenneville. STEPHANIE: And I'm Stephanie Minn, and together, we're here to share a bit of what we've learned along the way. JOËL: So, Stephanie, what's new in your world? STEPHANIE: So, today I went out for a coffee on my bike, and I feel like I finally have my perfect, like, on-the-go coffee setup. We have this thoughtbot branded travel mug. So, it's one of the little bits of swag that we got from the company. It's, like, perfectly leak-proof. I'll link the brand in the show notes. But it's perfectly leak-proof, which is great. And on my bike, I have a little stem bag, so it's just, like, a tiny kind of, like, cylindrical bag that sits on the, like, vertical part of my handlebars that connects to the rest of my bag. And it's just, like, the perfect size for a 12-ounce coffee. And so, I put my little travel mug in there, and I just had a very refreshing morning. And I'd gone out on my bike for a little bit, stopping by for coffee and headed home to work. And I got to drink my coffee during my first meeting. So, it was a wonderful way to start the day. JOËL: Do you just show up at the coffee shop with your refillable mug and say, "Hey, can you pour some coffee in this?" STEPHANIE: Yeah. I think a lot of coffee places are really amenable to bringing your own travel mugs. So yeah, it's really nice because I get to use less plastic. And also, you know, when you get a to-go mug, it is not leak-proof, right? It could just slosh all over the place and spill, so not bike-friendly. But yeah, bring your own mug. It's very easy. JOËL: Excellent. STEPHANIE: So, Joël, what's new in your world? JOËL: Also, warm beverages. Who would have thought? It's almost like it's cold in North America or something. I've been really enjoying making myself tea in the afternoons recently. And I've been drinking this brand of tea that is a little bit extra. Every flavor of tea they have comes with a description of how the tea feels. STEPHANIE: Ooh. JOËL: I don't know who came up with these, but they're kind of funny. So, one that I particularly enjoy is described as feels like stargazing on an empty beach. STEPHANIE: Wow. That's very specific. JOËL: They also give you tasting notes. This one has tastes of candied violet, elderberry, blackberry, and incense. STEPHANIE: Ooh, that sounds lovely. Are you drinking, like, herbal tea in the afternoon, or do you drink caffeinated tea? JOËL: I'll do caffeinated tea. I limit myself to one pot of coffee that I brew in the morning, and then, whenever that's done, I switch to tea. Tea I allow myself anything: herbal, black tea; that's fine. STEPHANIE: Yeah, I can't have too much caffeine in the afternoon either. But I do love an extra tea. I wish I could remember, like, what even was in this tea or what brand it was, but once I had a tea that was a purplish color. But then, when you squeeze some lemon in it, or I guess maybe anything with a bit of acid, it would turn blue. JOËL: Oh, that's so cool. STEPHANIE: Yeah, I'll have to find what this tea was [laughs] and update the podcast for any tea lovers out there. But yeah, it was just, like, a little bit of extra whimsy to your regular routine. JOËL: I love adding a little whimsy to my day, even if it's just seeing a random animated GIF that a coworker has sent or Tuple has some of the, like, reactions you can send if you're pairing with someone. And I don't use those very often, so whenever one of those comes through, and it's like, ship it or yay, that makes me very happy. STEPHANIE: Agreed. JOËL: This week is really fun because as we were prepping for this episode, we both realized that there is a lot that's been new in our world recently. And Stephanie, in particular, you've got some pretty big news that recently happened to you. STEPHANIE: Yeah, it turns out we're making the what's new in your world segment the entire episode today [laughs]. But my news is that I am speaking at RailsConf this year, so that is May 7th through 9th in Detroit. And so, yeah, I haven't spoken at a RailsConf before, only a RubyConf. So, I'm looking forward to it. My talk is called: So, Writing Tests Feels Painful. What now? JOËL: Wait, is writing tests ever painful [laughs]? STEPHANIE: Maybe not for you, but for the rest of us [laughs]. JOËL: No, it absolutely is. I, right before this recording, came from a pairing session where we were scratching our heads on an, like, awkward-to-write test. It happens to all of us. STEPHANIE: Yeah. So, I was brainstorming topics, and I kind of realized, especially with a lot of our consulting experience, you know, we hear from developers or even maybe, like, engineering managers a lot of themes around like, "Oh, like, development is slowing down because our test suite is such a headache," or "It's really slow. It's really flaky. It's really complicated." And that is a pain point that a lot of tech leaders are also looking to address for their teams. But I was really questioning this idea that, like, it always had to be some effort to improve the test suite, like, that had to be worked on at some later point or get, like, an initiative together to fix all of these problems, and that it couldn't just be baked into your normal development process, like, on an individual level. I do think it is really easy to feel a lot of pain when trying to write tests and then just be like, ugh, like, I wish someone would fix this, right? Or, you know, just kind of ignore the signals of that pain because you don't know, like, how to manage it yourself. So, my talk is about when you do feel that pain, really trying to determine if there's anything you can do, even in just, like, the one test file that you're working in to make things a little bit easier for yourself, so it doesn't become this, like, chronic issue that just gets worse and worse. Is there something you could do to maybe reorganize the file as you're working in it to make some conditionals a little bit clearer? Is there any, like, extra test setup that you're like, "Oh, actually, I don't need this anymore, and I can just start to get rid of it, not just for this one example, but for the rest in this file"? And do yourself a favor a little bit. So yeah, I'm excited to talk about that because I think that's perhaps, like, a skill that we don't focus enough on. JOËL: Are you going to sort of focus in on the side of things where, like, a classic TDD mantra is that test pain reflects underlying code complexity? So, are you planning to focus on the idea of, oh, if you're feeling test pain, maybe take some time to refactor some of the code that's under test, maybe because there's some tight coupling? Or are you going to lean a little bit more into maybe, like, the Boy Scout rule, you know, 'Leave the campsite cleaner than you found it' for your test files? STEPHANIE: Ooh, I like that framing. Definitely more of the former. But one thing I've also noticed working with a lot of client teams is that it's not always clear, like, how to refactor. I think a lot of intermediate developers start to feel that pain but don't know what to do about it. They don't know, like, maybe the code smells, or the patterns, or refactoring strategies, and that can certainly be taught. It will probably pull from that. But even if you don't know those skills yet, I'm wondering if there's, like, an opportunity to teach, like, developers at that level to start to reflect on the code and be like, "Hmm, what could I do to make this a little more flexible?" And they might not know the names of the strategies to, like, extract a class, but just start to get them thinking about it. And then maybe when they come across that vocabulary later, it'll connect a lot easier because they'll have started to think about, you know, their experiences day to day with some of the more conceptual stuff. JOËL: I really like that because I feel we've probably all heard that idea that test pain, especially when you're test driving, is a sign of maybe some anti-patterns or some code smells in the underlying code that you're testing. But translating that into something actionable and being able to say, "Okay, so my tests are painful. They're telling me something needs to be refactored. I'm looking at this code, and I don't know what to refactor." It's a big jump. It's almost the classic draw two circles; draw the rest of the owl meme. And so, I think bridging that gap is something that is really valuable for our community. STEPHANIE: Yeah, that's exactly what I hope to do in my talk. So, Joël, you [chuckles] also didn't quite mention that you have big news as well. JOËL: So, I also got accepted to speak at RailsConf. I'm giving a talk on Building a Dungeons & Dragons Character Sheet Using Turbo. STEPHANIE: That's really awesome. I'm excited because I want to learn more about Turbo. I want someone else to tell me [laughs] what I can do with it. And as a person with a little bit of Dungeons & Dragons experience, I think a character sheet is kind of the perfect vehicle for that. JOËL: Building a D&D character sheet has been kind of my go-to project to experiment with a new front-end framework because it's something that's pretty dynamic. And for those who don't know, there's a bunch of fields that you fill in with stats for different attributes that your character has, but then those impact other stats that get rendered. And sometimes there can be a chain two or three long where different numbers kind of combine together. And so, you've got this almost dependency tree of, like, a particular number. Maybe your skill at acrobatics might depend on a number that you entered in the dexterity field, but it also depends on your proficiency bonus, and maybe also depends on the race that you picked and a few other things. And so, calculating those numbers all of a sudden becomes not quite so simple. And so, I find it's a really fun exercise to build when trying out a new interactive front-end technology. STEPHANIE: Have you done this with a different implementation or a framework? JOËL: I've done this, not completely, but I've attempted some parts of a D&D character sheet, I think, with Backbone.js with Ember. I may have done an Angular one at some point in original Angular, so Angular 1. I did this with Elm. Somehow, I skipped React. I don't think I did React to build a D&D character sheet. And now I'm kind of moving a little bit back to the backend. How much can we get done just with Turbo? Or do we need to pull in maybe Stimulus? These are all things that are going to be really fun to demonstrate. STEPHANIE: Yeah. Speaking of injecting some whimsy earlier, I think it's kind of like just a little more fun than a regular to-do app, you know, or a blog to show how you can build, you know, something that people kind of understand with a different technology. JOËL: Another really fun thing that I've been toying with this week has been using AI to help me refactor code. And this has been using just sort of a classic chat AI, not a tool like Copilot. And I was dealing with a query that was really slow, and I wanted to restructure it in a different way. And I described to the AI how I wanted it to refactor and explicitly said, "I want this to be the same before and after." And I asked it to do the refactor, and it gave me some pretty disappointing results where it did some, like, a couple of really obvious things that were not that useful. And I was talking to a colleague about how I was really disappointed. I was thinking, well, AI should be able to do something better than this. And this colleague suggested changing the way I was asking for things and specifically asking for a step-by-step and asking it to prove every step using relational algebra, which is the branch of math that deals with everything that underlies relational databases, so the transformations that you would do where you keep everything the same, but you're saying, "Hey, these equations are all equivalent." And it sure did. It gave me a, like, 10-step process with all these, like, symbols and things. My relational algebra is not that strong, and so I couldn't totally follow along. But then I asked it to give me a code example, like, show me the SQL at every step of this transformation and at the end. And, you know, it all kind of looked all right. I've not fully tested the final result it gave me to see if it does what it says on the tin. But I'm cautiously optimistic. I think it looks very similar to something that I came up with on my own. And so, I'm somewhat impressed, at least, like, much better than things were in the beginning with that first round. So, I'm really curious to see where I can take this. STEPHANIE: Yeah, I think that's cool that you were able to prompt it differently and get something more useful. One of the reasons why I personally have been a little bit hesitant to get into the large language models is because I would love to see the AI show its work, essentially, like, tell me a little bit more about how it got from question to answer. And I thought that framing of kind of step-by-step show me code was a really interesting way, even to just, like, get some different results that do the same thing. But you can kind of evaluate that a little bit more on your own rather than just using that first result that it gave you that was like, eh, like, I don't know if this really did anything for me. So, it would be cool, even if you don't end up using, like, the final one, right? If something along the way also is an improvement from what you started with that would be really interesting. JOËL: Honestly, I think you kind of want the same thing if you're chatting with an AI chatbot or having a conversation in Slack with a colleague. They're just like, "Hey, can you help me refactor this?" And then a sort of, like, totally different chunk of code. And it's just like, "Trust me, it works." STEPHANIE: [laughs]. JOËL: And maybe it does. Maybe you plug it into your codebase and run the tests against it, and the tests are still green. And so, you trust that it works, but you don't really understand where it came from. That doesn't always feel good, even when it comes from a human. So, what I've appreciated with colleagues has been when they've given me a step-by-step. Sometimes, they give me the final product. They just say, "Hey. Try this. Does this work?" Plug it in to the test. It does pass. It's green. Great. "Tell me what black magic you did to get to that." And then they give me the step-by-step and it's like, oh, that's so good because not only do I get a better understanding of what happens at every step, but now I'm equipped the next time I run into this problem to apply the same technique to figure it out on my own. STEPHANIE: Yeah. And I liked, also, that relational algebra pro tip, right? It kind of ensures that what you're getting makes sense or is equivalent along the way [laughs]. JOËL: We think, right? I don't know enough relational algebra to check its work. It is quite possible that it is making some subtle mistakes along the way, or, like, making inferences that it shouldn't be. I'm not going to say I trust that. But I think, specifically, when asking for SQL transformations, prompting it to do so using relational algebra in a step-by-step way seemed to be a way to get it to do something more reliably or at least give more interesting results. STEPHANIE: Cool. JOËL: I was interested in trying this out in part because I've been more curious about AI tools recently, and also because we're hoping to do a deeper dive into AI on a Bike Shed episode at some point later, so very much still in the gathering information phase. But this was a really cool experience. So, having an AI refactor a query for me using relational algebra, definitely something that's new in my world this week. STEPHANIE: Speaking of refactoring and this idea of making improvements to your code and trying to figure out how to get from what you currently have to something new, I have been thinking a lot about how to make code reviews more actionable. And that's because, on my current client project, our team is struggling a little bit with code reviews, especially when you kind of want to give feedback on more of a design change in the code or thinking about some different abstractions. I have found that that is really hard to communicate async and also in a, like, a GitHub code review format where you can really just comment, like, line by line. And I've found that, you know, when someone is leaving feedback, that's like, "I'm having a hard time reading this. And I'm imagining that we could organize the code a bit differently in these three different layers or abstractions," there's a lot of assumptions there, right [laughs]? That your message is being communicated to the author and that they are able to, like, visualize, or have a mental model for what you're explaining as well. And then kind of what I've been seeing in this dynamic is, like, not really knowing what to do with that and to kind of just, like, I don't know where to go from here. So, I guess the next step is just to, like, merge it. Is that something you've experienced before or encountered when it comes to feedback? JOËL: Broader changes are often challenging to explain, especially when they're...sometimes you get so abstract you can just write a quick paragraph. And sometimes it's like, hey, what if we, like, totally change our approach? I've definitely done the thing where I'll just ping someone and say, "Hey, can we talk about this synchronously? Can we get on a call and have a deeper conversation?" How do you tend to approach if you're not going to hop on a call with someone and, like, have a 20 or 30-minute conversation? How do you approach doing that asynchronously on a pull request? Are you the type of person to put, like, a ton of, like, code blocks, like, "Here's what I was thinking. We could instead have this class and this thing"? And, like, pretty soon, it's, like, a page and a half of text. Or do you have another approach that you like to use? STEPHANIE: Yeah. And I think that's where it can get really interesting. Because my process is, I'll usually just start commenting and maybe if I'm seeing some things that can be done differently. If it's not just, like, a really obvious change that I could just use English to describe, I'll add a little suggested change. But I also don't want to just rewrite this person's code [laughs] in a code review. JOËL: That's the challenge, right? STEPHANIE: Yeah. And I've definitely seen that be done before, too. Once I notice I'm at, like, four plus comments, and then they're not just, like, nitpicks about, like, syntax or something like that, that helps me clue into the idea that there is some kind of bigger change that I might be asking of the author. And I don't want to overwhelm them with, like, individual comments that really are trying to convey something more holistic. JOËL: Right. I wonder if having a, like, specialized yet more abstract language is useful for these sorts of things where a whole paragraph in English or, you know, a ton of code examples might be a bit much. If you're able to say something like, "Hey, how would you feel about using a strategy pattern approach here instead of, you know, maybe a template object or some custom thing that we've built here?" that allows us to say a lot in a fairly sort of terse way. And it's the thing that you can leave more generically on the PR instead of, like, individually commenting in a bunch of places. And that can start a broader conversation at more of an architecture level. STEPHANIE: Yes, I really like that. That's a great idea. I would follow that up with, like, I think at the end of the day, there are some conversations that do need to be had synchronously. And so, I like the idea of leaving a comment like that and just kind of giving them resources to learn what a strategy pattern is and then offering support because that's also a way to shorten that feedback loop of trying to communicate an idea. And I like that it's kind of guiding them, but also you're there to add some scaffolding if it ends up being, like, kind of a big ask for them to figure out what to do. JOËL: There's also oftentimes, I think, a tone thing to manage where, especially if there's a difference in seniority or experience between the two people, it can be very easy for something to come across as an ask or a demand rather than a like, "Hey, let's think about some alternatives here." Or, like, "I have some concerns with your implementation. Let's sort of broadly explore some possible alternatives. Maybe a strategy pattern works." But the person reading that who wrote the original code might be, like, receiving that as "Your code is bad. You should have done a strategy pattern instead." And that's not the conversation I want to have, right? I want to have a back-and-forth about, "Hey, what are the trade-offs involved? Do you have a third architecture you'd like to suggest?" And so, that can be a really tricky thing to avoid. STEPHANIE: Yeah, I like that what you're saying also kind of suggested that it's okay if you don't have an idea yet for exactly how it should look like. Maybe you just are like, oh, like, I'm having a hard time understanding this, but I don't think just leaving it at that gives the author a lot to go on. I think there's something to it about maybe the action part of actionable is just like, "Can you talk about it with me?" Or "Could you explain what you're trying to do here?" Or, you know, leave a comment about what this method is doing. There's a lot of ways, I think, that you can reach some amount of improvement, even if it doesn't end up being, like, the ideal code that you would write. JOËL: Yes. There's also maybe a distinction in making it actionable by giving someone some code and saying, "Hey, you should copy-paste this code and make that..." or, you know, use a GitHub suggested code or something, which works on the small. And in the big, you can give some maybe examples and say, "Hey, what if you refactored in this way?" But sometimes, you could even step back and let them do that work and say, "Hey, I have some concerns with the current architecture. It's not flexible in the ways that we need to be flexible. Here's my understanding of the requirements. And here's sort of how I see maybe this architecture not working with that. Let's think of some different ways we could approach this problem." And oftentimes, it's nice to give at least one or two different ideas to help start that. But it can be okay to just ask the person, "Hey, can you come up with some alternate implementations that would fulfill these sets of requirements?" STEPHANIE: Yeah, I like that. And I can even see, like, maybe you do that work, and you don't end up pursuing it completely in addressing that feedback. But even asking someone to do the exercise itself, I think, can then spark new ideas and maybe other improvements. In general, I like to think about...I'm a little hesitant to use this metaphor because I'm not actually giving code, like, letter grades when I review them, but the idea that, like, not all code has to get, like, an A [chuckles], but maybe getting it, like, from one letter grade up to, like, half a letter grade, like, higher, that is valuable, even if it's not always practical to go through multiple rounds of code review. And I think just making it actionable enough to be a little bit better, like, that is, in my opinion, the sweet spot. JOËL: That's true. The sort of over-giving feedback to someone to try to get code perfect, rather than just saying, "Hey, can we make it slightly better?" And, you know, there are probably some minimum standards you need to hit. But at some point, it's a trade-off of like, how much time do we need to put polishing this versus shipping something? STEPHANIE: Yeah, and I think that it is cumulative over time, right? That's how people learn. Yeah, it's like one of the biggest opportunities for developers to level up is from that feedback. And that's why I think it's important that it's actionable because, you know, and you put the time into, like, giving that review, and it's not just to make sure the code works, but it's also, like, one of the touch points for collaboration. JOËL: So, if you had to summarize what makes code review comments actionable, do you have, like, top three tips that make a comment really actionable as opposed to something that's not helpful? Or maybe that's more of the journey that you're on, and you've not distilled it down to three pithy tips that you can put in a listicle. STEPHANIE: Honestly, I think it does kind of just distill down to one, which is for every comment, you should have an idea of what you would like the author to do about it. And it's okay if it's nothing, but then tell them that it's nothing. You could just be expressing, "I thought this was kind of weird, [laughs]" or "This is not my favorite thing, but it's okay." JOËL: And it can be okay for the thing you want the author to do. It doesn't have to be code. It could be a conversation. STEPHANIE: Yeah, exactly. It could be a conversation. It could be asking for information, too, right? Like, "Did you consider alternatives, and could you share them with me?" But that request portion, I think is really important because, yeah, I think there's so much miscommunication that can happen along the way. So, definitely still trying to figure out how to best support that kind of code review culture on my team. JOËL: This week's episode has been really fun because it's just been a combination of a lot of things that are new in our world, things that we've been trying, things that we've been learning. And kind of in an almost, like, a meta sense, one of the things I've been digging into is combinatorics, the branch of math that looks at how things combine and particularly how it works with combining a bunch of ActiveRecord query fragments where there's potential branching, so things like doing a union of two sort of sub queries or doing an or where you're combining two different where queries and trying to figure out what are the different paths through that. STEPHANIE: Wow, what a great way to combine what we were talking about, Joël [laughs]. Did you apply combinatorics to this podcast episode [laughs]? JOËL: Somehow, topics multiply with each other, something, something. STEPHANIE: Yeah, that makes sense to me [laughs]. Okay. Will you tell me more about what you've been using it for in your queries? JOËL: So, one thing I'm trying to do is because I've got these different branching paths through a query, I want to see sort of all the different ways because these are defined as ActiveRecord scopes, and I'm chaining them together. And it looks linear because I'm calling scope1 dot scope2 dot scope3. But each of those have branches inside of them. And so, there's all these different ways that data could get used or not. And one way that I figured out, like, what are the different paths here, was actually drawing out a matrix, just putting together a table. In this case, I had two scopes, each of which had a two-way branch inside, and so I made a two by two matrix. And that gave me all of the combinations of, oh, if you go down one branch in one scope and down another branch in the other scope. And what I went through is then I went in in each square and filled in how many records I would expect to get back from the query from some basic set that I was working on in each of these combinations. And one thing that was really interesting is that some of those combinations were sort of mutually exclusive, where a scope further down the line was filtering on the same field as an earlier one and would overwrite it or not overwrite it, but the two would then sort of you can't have both of those things be true at the same time. So, I'm looking for something that has a particular manager ID, and then I'm looking for something that has a particular different manager ID. And the way Rails combines these, if you just change scopes with where, is to and them together. There are no records that have both manager ID 1 and manager ID 2. You can only have one manager ID. And so, as I'm filling out my matrix, there's some sections I can just zero out and be like, wait, this will always return zero record. And then I can start focusing on the parts that are not zeroed out. So, I've got two or three squares. What's special about those? And that helped me really understand what the combination of these multiple query fragments together were actually trying to do as a holistic whole. STEPHANIE: Wow, yeah, that is really interesting because I hear you when you say it looks linear. And it would be really surprising to me for there to be branching paths. Like, that's not really what I think about when I think about SQL. But that makes a lot of sense that it could get so complicated that it's just impossible to get a certain kind of result. Like, what's going to be the outcome of applying combinatorics to this? Is there a refactoring opportunity, or is it really just to even understand what's going on? JOËL: So, this was a refactoring that I was trying to do, but I didn't really understand the underlying behavior of the chain of scopes. I just knew that they were doing some complex things that were inefficient from a SQL perspective. And so, I was looking at ways to refactor, but I also wanted to get a sense of what is this actually trying to do other than just chaining a bunch of random bits of code together? So, the matrix really helped for that. The other way that I used it was to write some tests because this query I was trying to refactor, this chain of scopes, was untested. And I wanted to write tests that were very thorough because I wanted to make sure that my refactor didn't break any edge cases. And I'm, you know, writing a few tests. Okay, well, here's a record that I definitely want to get returned by this query, and maybe here are a couple of records I don't want to get returned. And the more I was, like, going into this and trying to write test cases, the more I was finding more edge cases that I didn't want to and, oh, but what about this? And what about the combination of these things? And it got to the point where it was just messing with my mind. I was, like, confusing myself and really struggling to write tests that would do anything useful. STEPHANIE: Wow. Yeah. Honestly, I have already started to become a little bit suspicious of complex scopes, and this further pushes me in that direction [laughs] because yeah, once you start to...like, the benefit of them is that you can chain them, but it really hides a lot of the underlying behavior. So, you can easily just turn yourself around or, like, go, you know, kind of end up [laughs] in a little bit of a bind. JOËL: Definitely, especially once it grows a little bit harder to hold in your head. And I don't know exactly where that level is for me. But in this particular situation, I identified, I think, five different dimensions that would impact the results of this query. And then each dimension had maybe three or four different values that we might care about. And, eventually, I just took the time to write this out. So, I created five arrays and then just said, "Hey, here are the different managers that we care about. Here are the different project types we care about. Here are the different..." and we had, like, five of these, and each array had three or four elements in it. And then, in a series of nested loops, I iterated through all of these arrays and at the innermost loop, created the data that I wanted that matched that particular set of values. Now, we're often told you should not be doing things in nested loops because you end up sort of multiplying all of these together, but, in this case, this is actually what I wanted to do. You know, it turns out that I had a hundred-ish records I had to create to sort of create a data set that would be all the possible edge cases I might want to filter on. And creating them all by hand with all of the different variations was going to be too much. And so, I ended up doing this with arrays and nested loops. And it got me the data that I needed. And it gave me then the confidence to know that my refactor did indeed work the way I was expecting. STEPHANIE: Wow. That's truly hero's work [chuckles]. I'm, like, very excited because it sounds like that's a huge opportunity for some performance improvements as well. JOËL: For the underlying code, yes. The test might be a little bit slow because I'm creating a hundred records in the database. And you might say, "Oh, do you really need to do that? Can you maybe collapse some of these cases?" In this particular case, I really wanted to have high confidence that the refactor was not changing anything. And so, I was okay creating a hundred records over a series of nested iterations. That was a price I was willing to pay. The refactored query, it turns out, I was able to write it in a way that was significantly faster. STEPHANIE: Yeah, that's what I suspected. JOËL: So, I had to rewrite it in a way that didn't take advantage of all the change scopes. I had to just sort of write something custom from scratch, which is often the case, right? Performance and reusability sometimes fight against each other, and it's a trade-off. So, I'm not reusing the scopes. I had to write something from scratch, but it's multiple hundreds of times faster. STEPHANIE: Wow. Yeah. That seems worth it for a slow test [laughs] for the user experience to be a lot better, especially when you just reach that level of complexity. And it's a really awesome strategy that you applied to figure that out. I think it's a very unique one [laughs]. That's for sure. JOËL: I've had an interest in sort of analytical tools to help me understand domain models, to help understand problems, to help understand code that I'm working with for a while now, and I think an understanding of combinatorics fits into that. And then, particular tools within that, such as drawing things out in a table, in a two by two matrix, or an end-by-end matrix to get something visual, that's a great tool for debugging or understanding a problem. Thinking of problems as data that exists in multiple dimensions and then asking about the cardinality of that set it's the kind of analysis I did a lot when I was modeling using algebraic data types in Elm. But now I've sort of taken some of the tools and analysis I use from that world into thinking about things like SQL records, things like dealing with data in Ruby. And I'm able to bring those tools and that way of thinking to help me solve some problems that I might struggle to solve otherwise. For any of our listeners who this, like, kind of piques their interest, combinatorics falls under a broader umbrella of mathematics called discrete math. And within that, there's a lot that I think is really useful, a lot of tools and techniques that we can apply to our day-to-day programming. We have a Bike Shed episode where we talked about is discrete math relevant to day-to-day programmers and what are the ways it's so? We'll link that in the show notes. I also gave a talk at RailsConf last year diving into that titled: The Math Every Programmer Needs. So, if you're looking for something that's accessible to someone who's not done a math degree, those are two great jumping-off points. STEPHANIE: Yeah. And then, maybe you'll start drawing out arrays and applying combinatorics to figure out your performance problems. JOËL: On that note, shall we wrap up? STEPHANIE: Let's wrap up. Show notes for this episode can be found at bikeshed.fm. JOËL: This show has been produced and edited by Mandy Moore. STEPHANIE: If you enjoyed listening, one really easy way to support the show is to leave us a quick rating or even a review in iTunes. It really helps other folks find the show. JOËL: If you have any feedback for this or any of our other episodes, you can reach us @_bikeshed, or you can reach me @joelquen on Twitter. STEPHANIE: Or reach both of us at hosts@bikeshed.fm via email. JOËL: Thanks so much for listening to The Bike Shed, and we'll see you next week. ALL: Byeeeeeeeeee!!!!!!!! AD: Did you know thoughtbot has a referral program? If you introduce us to someone looking for a design or development partner, we will compensate you if they decide to work with us. More info on our website at: tbot.io/referral. Or you can email us at: referrals@thoughtbot.com with any questions.

    418: Mental Models For Reduce Functions

    Play Episode Listen Later Mar 12, 2024 42:50


    Joël talks about his difficulties optimizing queries in ActiveRecord, especially with complex scopes and unions, resulting in slow queries. He emphasizes the importance of optimizing subqueries in unions to boost performance despite challenges such as query duplication and difficulty reusing scopes. Stephanie discusses upgrading a client's app to Rails 7, highlighting the importance of patience, detailed attention, and the benefits of collaborative work with a fellow developer. The conversation shifts to Ruby's reduce method (inject), exploring its complexity and various mental models to understand it. They discuss when it's preferable to use reduce over other methods like each, map, or loops and the importance of understanding the underlying operation you wish to apply to two elements before scaling up with reduce. The episode also touches on monoids and how they relate to reduce, suggesting that a deep understanding of functional programming concepts can help simplify reduce expressions. Rails 7 EXPLAIN ANALYZE (https://www.bigbinary.com/blog/rails-7-1-adds-options-to-activerecord-relation-explain) Blocks, symbol to proc, and symbols arguments for reduce (https://thoughtbot.com/blog/blocks-procs-and-enumerable) Ruby tally (https://medium.com/@baweaver/ruby-2-7-enumerable-tally-a706a5fb11ea) Performance considerations for reduce in JavaScript (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce#when_to_not_use_reduce) Persistant data structures (https://www.youtube.com/watch?v=gTClDj9Zl1g) Avoid passing a block to map and reduce (https://thoughtbot.com/blog/avoid-putting-logic-in-map-blocks) Functional Programming with Bananas, Lenses, Envelopes and Barbed Wire (https://ris.utwente.nl/ws/portalfiles/portal/6142049/meijer91functional.pdf) monoids (https://blog.ploeh.dk/2017/10/06/monoids/) iteration anti-patterns (https://thoughtbot.com/blog/iteration-as-an-anti-pattern) Joël's talk on “constructor replacement” (https://www.youtube.com/watch?v=dSMB3rsufC8) Transcript:  STEPHANIE: Hello and welcome to another episode of The Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I'm Stephanie Minn. JOËL: And I'm Joël Quenneville. And together, we're here to share a bit of what we've learned along the way. STEPHANIE: So, Joël, what's new in your world? JOËL: I've been doing a bunch of fiddling with query optimization this week, and I've sort of run across an interesting...but maybe it's more of an interesting realization because it's interesting in the sort of annoying way. And that is that, using ActiveRecord scopes with certain more complex query pieces, particularly unions, can lead to queries that are really slow, and you have to rewrite them differently in a way that's not reusable in order to make them fast. In particular, if you have sort of two other scopes that involve joins and then you combine them using a union, you're unioning two sort of joins. Later on, you want to change some other scope that does some wares or something like that. That can end up being really expensive, particularly if some of the underlying tables being joined are huge. Because your database, in my case, Postgres, will pull a lot of this data into the giant sort of in-memory table as it's, like, building all these things together and to filter them out. And it doesn't have the ability to optimize the way it would on a more traditional relation. A solution to this is to make sure that the sort of subqueries that are getting unioned are optimized individually. And that can mean moving conditions that are outside the union inside. So, if I'm chaining, I don't know, where active is true on the outer query; on the union itself, I might need to move that inside each of the subqueries. So, now, in the two or three subqueries that I'm unioning, each of them needs to have a 'where active true' chained on it. STEPHANIE: Interesting. I have heard this about using ActiveRecord scopes before, that if the scopes are quite complex, chaining them might not lead to the most performant query. That is interesting. By optimizing the subqueries, did you kind of change the meaning of them? Was that something that ended up happening? JOËL: So, the annoying thing is that I have a scope that has the union in it, and it does some things sort of on its own. And it's used in some places. There are also other places that will try to take that scope that has the union on it, chain some other scopes that do other joins and some more filters, and that is horribly inefficient. So, I need to sort of rewrite the sort of subqueries that get union to include all these new conditions that only happen in this one use case and not in the, like, three or four others that rely on that union. So, now I end up with some, like, awkward query duplication in different call sites that I'm not super comfortable about, but, unfortunately, I've not found a good way to make this sort of nicely reusable. Because when you want to chain sort of more things onto the union, you need to shove them in, and there's no clean way of doing that. STEPHANIE: Yeah. I think another way I've seen this resolved is just writing it in SQL if it's really complex and it becoming just a bespoke query. We're no longer trying to use the scope that could be reusable. JOËL: Right. Right. In this case, I guess, I'm, like, halfway in between in that I'm using the ActiveRecord DSL, but I am not reusing scopes and things. So, I sort of have the, I don't know, naive union implementation that can be fine in all of the simpler use cases that are using it. And then the query that tries to combine the union with some other fancy stuff it just gets its own separate implementation different than the others that it has optimized. So, there are sort of two separate paths, two separate implementations. I did not drop down to writing raw SQL because I could use the ActiveRecord DSL. So, that's what I've been working with. What's new in your world this week? STEPHANIE: So, a couple of weeks ago, I think, I mentioned that I was working on a Rails 7 upgrade, and we have gotten it out the door. So, now the client application I'm working on is on Rails 7, which is exciting for the team. But in an effort to make the upgrade as incremental as possible, we did, like, back out of a few of the new application config changes that would have led us down a path of more work. And now we're kind of following up a little bit to try to turn some of those configs on to enable them. And it was very exciting to kind of, like, officially be on Rails 7. But I do feel like we tried to go for, like, the minimal amount of work possible in that initial big change. And now we're having to kind of backfill a little bit on some of the work that was a little bit more like, oh, I'm not really sure, like, how big this will end up being. And it's been really interesting work, I think, because it requires, like, two different mindsets. Like, one of them is being really patient and focused on tedious work. Like, okay, what happens when we enable this config option? Like, what changes? What errors do we see? And then having to turn it back off and then go in and fix them. But then another, I think, like, headspace that we have to be in is making decisions about what to do when we come to a crossroads around, like, okay, now that we are starting to see all the changes that are coming about from enabling this config, is this even what we want to do? And it can be really hard to switch between those two modes of thinking. JOËL: Yeah. How do you try to balance between the two? STEPHANIE: So, I luckily have been pairing with another dev, and I've actually found that to be really effective because he has, I guess, just, like, a little bit more of that patience to do the more tedious, mundane [laughs] aspects of, like, driving the code changes. And I have been riding along. But then I can sense, like, once he gets to the point of like, "Oh, I'm not sure if we should keep going down this road," I can step in a little bit more and be like, "Okay, like, you know, I've seen us do this, like, five times now, and maybe we don't want to do that." Or maybe being like, "Okay, we don't have a really clear answer, but, like, who can we talk to to find out a little bit more or get their input?" And that's been working really well for me because I've not had a lot of energy to do more of that, like, more manual or tedious labor [chuckles] that comes with working on that low level of stuff. So yeah, I've just been pleasantly surprised by how well we are aligning our superpowers. JOËL: To use some classic business speech, how does it feel to be in the future on Rails 7? STEPHANIE: Well, we're not quite up, you know, up to modern days yet, but it does feel like we're getting close. And, like, I think now we're starting to entertain the idea of, like, hmm, like, could we be even on main? I don't think it's really going to happen, but it feels a little bit more possible. And, in general, like, the team thinks that that could be, like, really exciting. Or it's easier, I think, once you're a little bit more on top of it. Like, the worst is when you get quite behind, and you end up just feeling like you're constantly playing catch up. It just feels a little bit more manageable now, which is good. JOËL: I learned this week a fun fact about Rails 7.1, in particular, which is that the analyze method on ActiveRecord queries, which allowed you to sort of get SQL EXPLAIN statements, now has the ability to pass in a couple of extra parameters. So, there are symbols, and you can pass in things like analyze or verbose, which allows you to get sort of more data out of your EXPLAIN query, which can be quite nice when you're debugging for performance. So, if you're in the future and you're on Rails 7.1 and you want sort of the in-depth query plans, you don't need to copy the SQL into a Postgres console to get access to the sort of fully developed EXPLAIN plan. You can now do it by passing arguments to EXPLAIN, which I'm very happy for. STEPHANIE: That's really nice. JOËL: So, we've mentioned before that we have a developers' channel on Slack here at thoughtbot, and there's all sorts of fun conversations that happen there. And there was one recently that really got me interested, where people were talking about Ruby's reduce method, also known as inject. And it's one of those methods that's kind of complicated, or it can be really confusing. And there was a whole thread where people were talking about different mental models that they had around the reduce method and how they sort of understand the way it works. And I'd be curious to sort of dig into each other's mental models of that today. To kick us off, like, how comfortable do you feel with Ruby's reduce method? And do you have any mental models to kind of hold it in your head? STEPHANIE: Yeah, I think reduce is so hard to wrap your head around, or it might be one of the most difficult, I guess, like, functions a new developer encounters, you know, in trying to understand the tools available to them. I always have to look up the order of the arguments [laughs] for reduce. JOËL: Every time. STEPHANIE: Yep. But I feel like I finally have a more intuitive sense of when to use it. And my mental model for it is collapsing a collection into one value, and, actually, that's why I prefer calling it reduce rather than the inject alias because reduce kind of signals to me this idea of going from many things to one canonical thing, I suppose. JOËL: Yeah, that's a very common use case for reducing, and I guess the name itself, reducing, kind of has almost that connotation. You're taking many things, and you're going to reduce that down to a single thing. STEPHANIE: What was really interesting to me about that conversation was that some people kind of had the opposite mental model where it made a bit more sense for them to think about injecting and, specifically, like, the idea of the accumulator being injected with values, I suppose. And I kind of realized that, in some ways, they're kind of antonyms [chuckles] a little bit because if you're focused on the accumulator, you're kind of thinking about something getting bigger. And that kind of blew my mind a little bit when I realized that, in some ways, they can be considered opposites. JOËL: That's really fascinating. It is really interesting, I think, the way that we can take the name of a method and then almost, like, tell ourselves a story about what it does that then becomes our way of remembering how this method works. And the story we tell for the same method name, or in this case, maybe there's a few different method names that are aliases, can be different from person to person. I know I tend to think of inject less in terms of injecting things into the accumulator and more in terms of injecting some kind of operator between every item in the collection. So, if we have an array of numbers and we're injecting plus, in my mind, I'm like, oh yeah, in between each of the numbers in the collection, just inject a little plus sign, and then do the math. We're summing all the items in the collection. STEPHANIE: Does that still hold up when the operator becomes a little more complex than just, you know, like, a mathematical operator, like, say, a function? JOËL: Well, when you start passing a block and doing custom logic, no, that mental model kind of falls apart. In order for it to work, it also has to be something that you can visualize as some form of infix operator, something that goes between two values rather than, like, a method name, which is typically in prefix position. I do want to get at this idea, though: the difference between sort of the block version versus passing. There are ways where you can just do a symbol, and that will call a method on each of the items. Because I have a bit of a hot take when it comes to writing reduce blocks or inject blocks that are more accessible, easier to understand. And that is, generally, that you shouldn't, or more specifically, you should not have a big block body. In general, you should be either using the symbol version or just calling a method within the block, and it's a one-liner. Which means that if you have some complex behavior, you need to find a way to move that out of this sort of collection operation and into instance methods on the objects being iterated. STEPHANIE: Hmm, interesting. By one-liner do you mean passing the name of the method as a proc or actually, like, having your block that then calls the method? Because I can see it becoming even simpler if you have already extracted a method. JOËL: Yeah, if you can do symbol to proc, that's amazing, or even if you can use just the straight-up symbol way of invoking reduce or inject. That typically means you have to start thinking about the types of objects that you are working with and what methods can be moved onto them. And sometimes, if you're working with hashes or something like that that don't have domain methods for what you want, that gets really awkward. And so, then maybe that becomes maybe a hint that you've got some primitive obsession happening and that this hash that sort of wants a domain object or some kind of domain method probably should be extracted to its own object. STEPHANIE: I'll do you with another kind of spicy take. I think, in that case, maybe you don't want a reduce at all. If you're starting to find that...well, okay, I think it maybe could depend because there could be some very, like, domain-specific logic. But I have seen reduce end up being used to transform the structure of the initial collection when either a different higher-order function can be used or, I don't know, maybe you're just better off writing it with a regular loop [laughs]. It could be clearer that way. JOËL: Well, that's really interesting because...so, you mentioned the idea that we could use a different higher-order function, and, you know, higher-order function is that fancy term, just a method that accepts another method as an argument. In Ruby, that just means your method accepts a block. Reduce can be used to implement pretty much the entirety of enumerable. Under the hood, enumerable is built in terms of each. You could implement it in terms of reduce. So, sometimes it's easy to re-implement one of the enumerable methods yourself, accidentally, using reduce. So, you've written this, like, complex reduce block, and then somebody in review comes and looks at it and is like, "Hey, you realize that's just map. You've just recreated map. What if we used map here?" STEPHANIE: Yeah. Another one I've seen a lot in JavaScript land where there are, you know, fewer utility functions is what we now have in Ruby, tally. I feel like that was a common one I would see a lot when you're trying to count instances of something, and I've seen it done with reduce. I've seen it done with a for each. And, you know, I'm sure there are libraries that actually provide a tally-like function for you in JS. But I guess that actually makes me feel even more strongly about this idea that reduce is best used for collapsing something as opposed to just, like, transforming a data structure into something else. JOËL: There's an interesting other mental model for reduce that I think is hiding under what we're talking about here, and that is the idea that it is a sort of mid-level abstraction for dealing with collections, as opposed to something like map or select or some of those other enumerable helpers because those can all be implemented in terms of reduce. And so, in many cases, you don't need to write the reduce because the library maintainer has already used reduce or something equivalent to build these higher-level helpers for you. STEPHANIE: Yeah, it's kind of in that weird point between, like, very powerful [chuckles] so that people can start to do some funky things with it, but also sometimes just necessary because it can feel a little bit more concise that way. JOËL: I've done a fair amount of functional programming in languages like Elm. And there, if you're building a custom data structure, the sort of lowest-level way you have of looping is doing a recursion, and recursions are messy. And so, what you can do instead as a library developer is say, "You know what, I don't want to be writing recursions for all of these." I don't know; maybe I'm building a tree library. I don't want to write a recursion for every different function that goes over trees if I want to map or filter or whatever. I'm going to write reduce using recursion, and then everything else can be written in terms of reduce. And then, if people want to do custom things, they don't need to recurse over my tree. They can use this reduce function, which allows them to do most of the traversals they want on the tree without needing to touch manual recursion. So, there's almost, like, a low-level, mid-level, high-level in the library design, where, like, lowest level is recursion. Ideally, nobody touches that. Mid-level, you've got reducing that's built out on top of recursion. And then, on top of that, you've got all sorts of other helpers, like mapping, like filtering, things like that. STEPHANIE: Hmm. I'm wondering, do you know of any performance considerations when it comes to using reduce built off a recursion? JOËL: So, one of the things that can be really nice is that writing a recursion yourself is dangerous. It's so easy to, like, accidentally introduce Stack Overflow. You could also write a really inefficient one. So, ideally, what you do is that you write a reduce that is safe and that is fast. And then, everybody else can just use that to not have to worry about the sort of mechanics of traversing the collection. And then, just use this. It already has all of the safety and speed features built in. You do have to be careful, though, because reduce, by nature, traverses the entire collection. And if you want to break out early of something expensive, then reduce might not be the tool for you. STEPHANIE: I was also reading a little bit about how, in JavaScript, a lot of developers like to stick to that idea of a pure function and try to basically copy the entire accumulator for every iteration and creating a new object for that. And that has led to some memory issues as well. As opposed to just mutating the accumulator, having, especially when you, you know, are going through a collection, like, really large, making that copy every single time and creating, yeah [chuckles], just a lot of issues that way. So, that's kind of what prompted that question. JOËL: Yeah, that can vary a lot by language and by data structure. In more functional languages that try to not mutate, they often have this idea of what they call persistent data structures, where you can sort of create copies that have small modifications that don't force you to copy the whole object under the hood. They're just, like, pointers. So, like, hey, we, like, are the same as this other object, but with this extra element added, or something like that. So, if you're growing an array or something like that, you don't end up with 10,000 copies of the array with, like, a new element every time. STEPHANIE: Yeah, that is interesting. And I feel like trying to adopt different paradigms for different tools, you know, is not always as straightforward as some wish it were [laughs]. JOËL: I do want to give a shout-out to an academic paper that is...it is infamously dense. The title of it is Functional Programming with Bananas, Lenses, and Barbed Wire. STEPHANIE: It doesn't sound dense; it sounds fun. Well, I don't about barbed wire. JOËL: It sounds fun, right? STEPHANIE: Yeah, but certainly quirky [laughs]. JOËL: It is incredibly dense. And they've, like, created this custom math notation and all this stuff. But the idea that they pioneered there is really cool, this idea that kind of like I was talking about sort of building libraries in different levels. Their idea is that recursion is generally something that's unsafe and that library and language designers should take care of all of the recursion and instead provide some of these sort of mid-level helper methods to do things. Reducing is one of them, but their proposal is that it's not the only one. There's a whole sort of family of similar methods that are there that would be useful in different use cases. So, reduce allows you to sort of traverse the whole thing. It does not allow you to break out early. It does not allow you to keep sort of track of a sort of extra context element if you want to, like, be traversing a collection but have a sort of look forward, look back, something like that. So, there are other variations that could handle those. There are variations that are the opposite of reduce, where you're, like, inflating, starting from a few parameters and building a collection out of them. So, this whole concept is called recursion schemes, and you can get, like, really deep into some theory there. You'll hear fancy words like catamorphisms and anamorphisms. There's a whole world to explore in that area. But at its core, it's this idea that you can sort of slice up things into this sort of low-level recursion, mid-level helpers, and then, like, kind of userland helpers built on top of that. STEPHANIE: Wow. That is very intense; it sounds like [chuckles]. I'm happy not to ever have to write a recursion ever again, probably [laughs]. Have you ever, as just a web developer in your day-to-day programming, found a really good use case for dropping down to that level? Or are you kind of convinced that, like, you won't really ever need to? JOËL: I think it depends on the paradigm of the language you're working in. In Ruby, I've very rarely needed to write a recursion. In something like Elm, I've had to do that, eh, not infrequently. Again, it depends, like, if I'm doing more library-esque code versus more application code. If I'm writing application code and I'm using an existing, let's say, tree library, then I typically don't need to write a recursion because they've already written traversals for me. If I'm making my own and I have made my own tree libraries, then yes, I'm writing recursions myself and building those traversals so that other people don't have to. STEPHANIE: Yeah, that makes sense. I'd much rather someone who has read that paper [laughs] write some traversal methods for me. JOËL: And, you know, for those who are curious about it, we will put a link to this paper in the description. So, we've talked about a sort of very academic mental model way of thinking about reducing. I want to shift gears and talk about one that I have found is incredibly practical, and that is the idea that reduce is a way to scale an operation that works on two objects to an operation that works on sort of an unlimited number of objects. To make it more concrete, take something like addition. I can add two numbers. The plus operator allows me to take one number, add another, get a sum. But what if I want to not just add two numbers? I want to add an arbitrary number of numbers together. Reduce allows me to take that plus operator and then just scale it up to as many numbers as I want. I can just plug that into, you know, I have an array of numbers, and I just call dot reduce plus operator, and, boom, it can now scale to as many numbers as I want, and I can sum the whole thing. STEPHANIE: That dovetails quite nicely with your take earlier about how you shouldn't pass a block to reduce. You should extract that into a method. Don't you think? JOËL: I think it does, yes. And then maybe it's, like, sort of two sides of a coin because I think what this leads to is an approach that I really like for reducing because sometimes, you know, here, I'm starting with addition. I'm like, oh, I have addition. Now, I want to scale it up. How do I do that? I can use reduce. Oftentimes, I'm faced with sort of the opposite problem. I'm like, oh, I need to add all these numbers together. How do I do that? I'm like, probably with a reduce. But then I start writing the block, and, like, I get way too into my head about the accumulator and what's going to happen. So, my strategy for writing reduce expressions is to, instead of trying to figure out how to, like, do the whole thing together, first ask myself, how do I want to combine any two elements that are in the array? So, I've got an array of numbers, and I want to sum them all. What is the thing I need to do to combine just two of those? Forget the array. Figure that out. And then, once I have that figured out, maybe it's an existing method like plus. Maybe it's a method I need to define on it if it's a custom object. Maybe it's a method that I write somewhere. Then, once I have that, I can say, okay, I can do it for two items. Now, I'm going to scale it up to work for the whole array, and I can plug it into reduce. And, at that point, the work is already basically done, so I don't end up with a really complex block. I don't end up, like, almost ending in, like, a recursive infinite loop in my head because I do that. STEPHANIE: [laughs]. JOËL: So, that approach of saying, start by figuring out what is the operation you want to do to combine two elements, and then use reduce as a way to scale that to your whole array is a way that I've used to keep things simple in my mind. STEPHANIE: Yeah, I like that a lot as a supplement to the model I shared earlier because, for me, when I think about reducing as, like, collapsing into a value, you kind of are just like, well, okay, I start with the collection, and then somehow I get to my single value. But the challenge is figuring out how that happens [laughs], like, the magic that happens in between that. And I think another alias that we haven't mentioned yet for reduce that is used in a lot of other languages is fold. And I actually like that one a lot, and I think it relates to your mental model. Because when I think about folding, I'm picturing folding up a paper like an accordion. And you have to figure out, like, what is the first fold that I can make? And just repeating that over and over to get to your little stack of accordion paper [laughs]. And if you can figure out just that first step, then you pretty much, like, have the recipe for getting from your initial input to, like, your desired output. JOËL: Yeah. I think fold is interesting in that some languages will make a distinction between fold and reduce. They will have both. And typically, fold will require you to pass an initial value, like a starting accumulator, to start it off. Whereas reduce will sort of assume that your array can use the first element of the array as the first accumulator. STEPHANIE: Oh, I just came up with another visual metaphor for this, which is, like, folding butter into croissant pastry when the butter is your initial value [laughs]. JOËL: And then the crust is, I guess, the elements in the array. STEPHANIE: Yeah. Yeah. And then you get a croissant out of it [laughs]. Don't ask me how it gets to a perfectly baked, flaky, beautiful croissant, but somehow that happens [laughs]. JOËL: So, there's an interesting sort of subtlety here that I think happens because there are sort of two slightly different ways that you can interact with a reduce. Sometimes, your accumulator is of the same type as the elements in your array. So, you're summing an array of numbers, and your accumulator is the sum, but each of the elements in the array are also numbers. So, it's numbers all the way through. And sometimes, your accumulator has a different type than the items in the array. So, maybe you have an array of words, and you want to get the sum of all of the characters and all the words. And so, now your accumulator is a number, but each of the items in the array are strings. STEPHANIE: Yeah, that's an interesting distinction because I think that's where you start to see the complex blocks being passed and reduced. JOËL: The complex blocks, definitely; I think they tend to show up when your accumulator has a different type than the individual items. So, maybe that's, like, a slightly more complicated use case. Oftentimes, too, the accumulator ends up being some, like, more complex, like, hash or something that maybe would really benefit from being a custom object. STEPHANIE: I've never done that before, but I can see why that would be really useful. Do you have an example of when you used a custom object as the accumulator? JOËL: So, I've done it for situations where I'm working with objects that are doing tally-like operations, but I'm not doing just a generic tally. There's some domain-specific stuff happening. So, it's some sort of aggregate counter on multiple dimensions that you can use, and that can get really ugly. And you can either do it with a reduce or you can have some sort of, like, initial version of the hash outside and do an each and mutate the hash and stuff like that. All of these tend to be a little bit ugly. So, in those situations, I've often created some sort of custom object that has some instance methods that allow to sort of easily add new elements to it. STEPHANIE: That's really interesting because now I'm starting to think, what if the elements in the collection were also a custom object? [chuckles] And then things could, I feel like, could be really powerful [laughs]. JOËL: There's often a lot of value, right? Because if the items in the collection are also a custom object, you can then have methods on them. And then, again, the sort of complexity of the reduce can sort of, like, fade away because it doesn't own any of the logic. All it does is saying, hey, there's a thing you can do to combine two items. Let's scale it up to work on a collection of items. And now you've sort of, like, really simplified what logic is actually owned inside the reduce. I do want to shout out for those listeners who are theory nerds and want to dig into this. When you have a reduce, and you've got an operation where all the values are of the same type, including the accumulator, typically, what you've got here is some form of monoid. It may be a semigroup. So, if you want to dig into some theory, those are the words to Google and to go a deep dive on. The main thing about monoids, in particular, is that monoids are any objects that have both a sort of a base case, a sort of empty version of themselves, and they have some sort of combining method that allows you to combine two values of that type. If your object has these things and follows...there's a few rules that have to be true. You have a monoid. And they can then be sort of guaranteed to be folded nicely because you can plug in their base case as your initial accumulator. And you can plug in their combining method as just the value of the block, and everything else just falls into place. A classic here is addition for numbers. So, if you want to add two numbers, your combining operator is a plus. And your sort of empty value is a zero. So, you would say, reduce initial value is zero, array of numbers. And your block is just plus, and it won't sum all of the numbers. You could do something similar with strings, where you can combine strings together with plus, and, you know, your empty string is your base case. So, now you're doing sort of string concatenation over arbitrary number of strings. Turns out there's a lot of operations that fall into that, and you can even define some of those on your custom object. So, you're like, oh, I've got a custom object. Maybe I want some way of, like, combining two of them together. You might be heading in the direction of doing something that is monoidal, and if so, that's a really good hint to know that it can sort of, like, just drop into place with a fold or a reduce and that that is a tool that you have available to you. STEPHANIE: Yeah, well, I think my eyes, like, widened a little bit when you first dropped the term monoid [laughs]. I do want to spend the last bit of our time talking about when not to use reduce, and, you know, we did talk a lot about recursion. But when do you think a regular old loop will just be enough? JOËL: So, you're suggesting when would you want to use something like an each rather than a reduce? STEPHANIE: Yeah. In my mind, you know, you did offer, like, a lot of ways to make reduce simpler, a lot of strategies to end up with some really nice-looking syntax [chuckles], I think. But, oftentimes, I think it can be equally as clear storing your accumulator outside of the iteration and that, like, is enough for me to understand. And reduce takes a little bit of extra overhead to figure out what I'm looking at. Do you have any thoughts about when you would prefer to do that? Or do you think that you would usually reach for something else? JOËL: Personally, I generally don't like the pattern of using each to iterate over a collection and then mutate some external accumulator. That, to me, is a bit of a code smell. It's a sign that each is not quite powerful enough to do the thing that I want to do and that I'm probably needing some sort of more specialized form of iteration. Sometimes, that's reduce. Oftentimes, because each can suffer from the same problem you mentioned from reduce, where it's like, oh, you're doing this thing where you mutate an external accumulator. Turns out what you're really doing is just map. So, use map or use select or, you know, some of the other built-in iterators from the enumerable library. There's a blog post on the thoughtbot blog that I continually link to people. And when I see the pattern of, like, mutating an external variable with each, yeah, I tend to see that as a bit of a code smell. I don't know that I would never do it, but whenever I see that, it's a sign to me to, like, pause and be like, wait a minute, is there a better way to do this? STEPHANIE: Yeah, that's fair. I like the idea that, like, if there's already a method available to you that is more specific to go with that. But I also think that sometimes I'd rather, like, come across that pattern of mutating a variable outside of the iteration over, like, someone trying to do something clever with the reduce. JOËL: Yeah, I guess reduce, especially if it's got, like, a giant block and you've got then, like, things in there that break or call next to skip iterations and things like that, that gets really mind-bending really quickly. I think a case where I might consider using an each over a reduce, and that's maybe generally when I tend to use each, is when I'm doing side effects. If I'm using a reduce, it's because I care about the accumulated value at the end. If I'm using each, it's typically because I am trying to do some amount of side effects. STEPHANIE: Yeah, that's a really good call out. I had that written down in my notes, and I'm glad you brought it up because I've seen them get conflated a little bit, and perhaps maybe that's the source of the pain that I'm talking about. But I really like that heuristic of reduce as, you know, you're caring about the output, as opposed to what's going on inside. Like, you don't want any unexpected behavior. JOËL: And I think that applies to something like map as well. My sort of heuristic is, if I'm doing side effects, I want each. If I want transformed values that are sort of one-to-one in the collection, I want map. If I want a single sort of aggregate value, then I want reduce. STEPHANIE: I think that's the cool thing about mixing paradigms sometimes, where all the strategies you talked about in terms of, you know, using custom, like, objects for your accumulator, or the elements in your collection, like, that's something that we get because, you know, we're using an object-oriented language like Ruby. But then, like, you also are kind of bringing the functional programming lens to, like, when you would use reduce in the first place. And yeah, I am just really excited now [chuckles] to start looking for some places I can use reduce after this conversation and see what comes out of it. JOËL: I think I went on a bit of an interesting journey where, as a newer programmer, reduce was just, like, really intense. And I struggled to understand it. And I was like, ban it from code. I don't want to ever see it. And then, I got into functional programming. I was like, I'm going to do reduce everywhere. And, honestly, it was kind of messy. And then I, like, went really deep on a lot of functional theory, and I think understood some things that then I was able to take back to my code and actually write reduce expressions that are much simpler so that now my heuristic is like, I love reduce; I want to use it, but I want as little as possible in the reduce itself. And because I understand some of these other concepts, I have the ability to know what things can be extracted in a way that will feel very natural, in a way that myself from five years ago would have just been like, oh, I don't know. I've got this, you know, 30-line reduce expression that I know is complicated, but I don't know how to improve. And so, a little bit of the underlying theory, I don't think it's necessary to understand these simplified reduces, but as an author who's writing them, I think it helps me write reduces that are simpler. So, that's been my journey using reduce. STEPHANIE: Yeah. Well, thanks for sharing. And I'm really excited. I hope our listeners have learned some new things about reduce and can look at it from a different light. JOËL: There are so many different perspectives. And I think we keep discovering new mental models as we talk to different people. It's like, oh, this particular perspective. And there's one that we didn't really dig into but that I think makes more sense in a functional world that's around sort of deconstructing a structure and then rebuilding it with different components. The shorthand name of this mental model, which is a fairly common one, is constructor replacement. For anyone who's interested in digging into that, we'll link it in the show notes. I gave a talk at an Elm meetup where I sort of dug into some of that theory, which is really interesting and kind of mind-blowing. Not as relevant, I think, for Rubyists, but if you're in a language that particularly allows you to build custom structures out of recursive types or what are sometimes called algebraic data types, or tagged unions, or discriminated unions, this thing goes by a bajillion names, that is a really interesting other mental model to look at. And, again, I don't think the list that we've covered today is exhaustive. You know, I would love it for any of our listeners; if you have your own mental models for how to think about folding, injecting, reducing, send them in: hosts@bikeshed.fm. We'd love to hear them. STEPHANIE: And on that note, shall we wrap up? JOËL: Let's wrap up. STEPHANIE: Show notes for this episode can be found at bikeshed.fm. JOËL: This show has been produced and edited by Mandy Moore. STEPHANIE: If you enjoyed listening, one really easy way to support the show is to leave us a quick rating or even a review in iTunes. It really helps other folks find the show. JOËL: If you have any feedback for this or any of our other episodes, you can reach us @_bikeshed, or you can reach me @joelquen on Twitter. STEPHANIE: Or reach both of us at hosts@bikeshed.fm via email. JOËL: Thanks so much for listening to The Bike Shed, and we'll see you next week. ALL: Byeeeeeeee!!!!!!! AD: Did you know thoughtbot has a referral program? If you introduce us to someone looking for a design or development partner, we will compensate you if they decide to work with us. More info on our website at: tbot.io/referral. Or you can email us at referrals@thoughtbot.com with any questions.

    417: Module Docs

    Play Episode Listen Later Mar 5, 2024 39:32


    Stephanie shares about her vacation at Disney World, particularly emphasizing the technological advancements in the park's mobile app that made her visit remarkably frictionless. Joël had a conversation about a topic he loves: units of measure, and he got to go deep into the idea of dimensional analysis with someone this week. Together, Joël and Stephanie talk about module documentation within software development. Joël shares his recent experience writing module docs for a Ruby project using the YARD documentation system. He highlights the time-consuming nature of crafting good documentation for each public method in a class, emphasizing that while it's a demanding task, it significantly benefits those who will use the code in the future. They explore the attributes of good documentation, including providing code examples, explaining expected usage, suggesting alternatives, discussing edge cases, linking to external resources, and detailing inputs, outputs, and potential side effects. Multidimensional numbers episode (https://bikeshed.thoughtbot.com/416) YARD docs (https://yardoc.org/) New factory_bot documentation (https://thoughtbot.com/blog/new-docs-for-factory_bot) Dash (https://kapeli.com/dash) Solargraph (https://solargraph.org/) Transcript:  JOËL: Hello and welcome to another episode of The Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I'm Joël Quenneville. STEPHANIE: And I'm Stephanie Minn, and together, we're here to share a bit of what we've learned along the way. JOËL: So, Stephanie, what's new in your world? STEPHANIE: So, I recently was on vacation, and I'm excited [chuckles] to tell our listeners all about it. I went to Disney World [laughs]. And honestly, I was especially struck by the tech that they used there. As a person who works in tech, I always kind of have a little bit of a different experience knowing a bit more about software, I suppose, than just your regular person [laughs], citizen. And so, at Disney World, I was really impressed by how seamlessly the like, quote, unquote, "real life experience" integrated with their use of their branded app to pair with, like, your time at the theme park. JOËL: This is, like, an app that runs on your mobile device? STEPHANIE: Yeah, it's a mobile app. I haven't been to Disney in a really long time. I think the last time I went was just as a kid, like, this was, you know, pre-mobile phones. So, I recall when you get into the line at a ride, you can skip the line by getting what's called a fast pass. And so, you kind of take a ticket, and it tells you a designated time to come back so that you could get into the fast line, and you don't have to wait as long. And now all this stuff is on your mobile app, and I basically did not wait in [laughs] a single line for more than, like, five minutes to go on any of the rides I wanted. It just made a lot of sense that all these things that previously had more, like, physical touchstones, were made a bit more convenient. And I hesitate to use the word frictionless, but I would say that accurately describes the experience. JOËL: That's kind of amazing; the idea that you can use tech to make a place that's incredibly busy also feel seamless and where you don't have to wait in line. STEPHANIE: Yeah and, actually, I think the coolest part was it blended both your, like, physical experience really well with your digital one. I think that's kind of a gripe I have as a technologist [laughs] when I'm just kind of too immersed in my screen as opposed to the world around me. But I was really impressed by the way that they managed to make it, like, a really good supplement to your experience being there. JOËL: So, you're not hyped for a future world where you can visit Disney in VR? STEPHANIE: I mean, I just don't think it's the same. I rode a ride [laughs] where it was kind of like a mini roller coaster. It was called Expedition Everest. And there's a moment, this is, like, mostly indoors, but there's a moment where the roller coaster is going down outside, and you're getting that freefall, like, drop feeling in your stomach. And it also happened to be, like, drizzling that day that we were out there, and I could feel it, you know, like, pelting my head [laughs]. And until VR can replicate that experience [chuckles], I still think that going to Disney is pretty fun. JOËL: Amazing. STEPHANIE: So, Joël, what's new in your world? JOËL: I'm really excited because I had a conversation about a topic that I like to talk about: units of measure. And I got to go deep into the idea of dimensional analysis with someone this week. This is a technique where you can look at a calculation or a function and sort of spot-check whether it's correct by looking at whether the unit for the measure that would come out match what you would expect. So, you do math on the units and ignore the numbers coming into your formula. And, you know, let's say you're calculating the speed of something, and you get a distance and the amount of time it took you to take to go that distance. And let's say your method implements this as distance times time. Forget about doing the actual math with the numbers here; just look at the units and say, okay, we've got our meters, and we've got our seconds, and we're multiplying them together. The unit that comes out of this method is meters times seconds. You happen to know that speeds are not measured in meters times seconds. They're measured in meters divided by seconds or meters per second. So, immediately, you get a sense of, like, wait a minute, something's wrong here. I must have a bug in my function. STEPHANIE: Interesting. I'm curious how you're representing that data to, like, know if there's a bug or not. In my head, when you were talking about that, I'm like, oh yeah, I definitely recall doing, like, math problems for homework [laughs] where I had, you know, my meters per second. You have your little fractions written out, and then when you multiply or divide, you know how to, like, deal with the units on your piece of paper where you're showing your work. But I'm having a hard time imagining what that looks like as a programmer dealing with that problem. JOËL: You could do it just all in your head based off of maybe some comments that you might have or the name of the variable or something. So, you're like, okay, well, I have a distance in meters and a time in seconds, and I'm multiplying the two. Therefore, what should be coming out is a value that is in meters times seconds. If you want to get fancier, you can do things with value objects of different types. So, you say, okay, I have a distance, and I have a time. And so, now I have sort of a multiplication of a distance and a time, and sort of what is that coming out as? That can sometimes help you prevent from having some of these mistakes because you might have some kind of error that gets raised at runtime where it's like, hey, you're trying to multiply two units that shouldn't be multiplied, or whatever it is. You can also, in some languages, do this sort of thing automatically at the type level. So, instead of looking at it yourself and sort of inferring it all on your own based off of the written code, languages like F# have built-in unit-of-measure systems where once you sort of tag numbers as just being of a particular unit of measure, any time you do math with those numbers, it will then tag the result with whatever compound unit comes from that operation. So, you have meters, and you have seconds. You divide one by the other, and now the result gets tagged as meters per second. And then, if you have another calculation that takes the output of the first one and it comes in, you can tell the compiler via type signature, hey, the input for this method needs to be in meters per second. And if the other calculation sort of automatically builds something that's of a different unit, you'll get a compilation error. So, it's really cool what it can do. STEPHANIE: Yeah, that is really neat. I like all of those built-in guardrails, I suppose, to help you, you know, make sure that your answer is correct. Definitely could have used that [chuckles]. Turns out I just needed a calculator to take my math test with [laughs]. JOËL: I think what I find valuable more than sort of the very rigorous approach is the mindset. So, anytime you're dealing with numbers, thinking in your mind, what is the unit of this number? When I do math with it with a different number, is it the same unit? Is it a different unit? What is the unit of the thing that's coming out? Does this operation make sense in the domain of my application? Because it's easy to sometimes think you're doing a math operation that makes sense, and then when you look at the unit, you're like, wait a minute, this does not make sense. And I would go so far as to say that, you know, you might think, oh, I'm not doing a physics app. I don't care about units of measure. Most numbers in your app that are actually numbers are going to have some kind of unit of measure associated to them. Occasionally, you might have something where it's just, like, a straight-up, like, quantity or something like that. It's a dimensionless number. But most things will have some sort of unit. Maybe it's a number of dollars. Maybe it is an amount of time, a duration. It could be a distance. It could be all sorts of things. Typically, there is some sort of unit that should attach to it. STEPHANIE: Yeah. That makes sense that you would want to be careful about making sure that your mathematical operations that you're doing when you're doing objects make sense. And we did talk about this in the last episode about multidimensional numbers a little bit. And I suppose I appreciate you saying that because I think I have mostly benefited from other people having thought in that mindset before and encoding, like I mentioned, those guardrails. So, I can recall an app where I was working with, you know, some kind of currency or money object, and that error was raised when I would try to divide by zero because rather than kind of having to find out later with some, not a number or infinite [laughs] amount of money bug, it just didn't let me do that. And that wasn't something that I had really thought about, you know, I just hadn't considered that zero value edge case when I was working on whatever feature I was building. JOËL: Yeah, or even just generally the idea of dividing money. What does that even mean? Are you taking an amount of money and splitting it into two equivalent piles to split among multiple people? That kind of makes sense. Are you dividing money by another money value? That's now asking a very different kind of question. You're asking, like, what is the ratio between these two, I guess, piles of money if we want to make it, you know, in the physical world? Is that a thing that makes sense in your application? But also, realize that that ratio that you get back is not itself an amount of money. And so, there are some subtle bugs that can happen around that when you don't keep track of what your quantities are. So, this past week, I've been working on a project where I ended up having to write module docs for the code in question. This is a Ruby project, so I'm writing docs using the YARD documentation system, where you effectively just write code comments at the sort of high level covering the entire class and then, also, individual documentation comments on each of the methods. And that's been really interesting because I have done this in other languages, but I'd never done it in Ruby before. And this is a piece of code that was kind of gnarly and had been tricky for me to figure out. And I figured that a couple of these classes could really benefit from some more in-depth documentation. And I'm curious, in your experience, Stephanie, as someone who's writing code, using code from other people, and who I assume occasionally reads documentation, what are the things that you like to see in good sort of method-level docs? STEPHANIE: Personally, I'm really only reading method-level docs when, you know, at this point, I'm, like, reaching for a method. I want to figure out how to use it in my use case right now [laughs]. So, I'm going to search API documentation for it. And I really am just scanning for inputs, especially, I think, and maybe looking at, you know, some potential various, like, options or, like, variations of how to use the method. But I'm kind of just searching for that at a glance and then moving on [laughs] with my day. That is kind of my main interaction with module docs like that, and especially ones for Ruby and Rails methods. JOËL: And for clarity's sake, I think when we're talking about module docs here, I'm generally thinking of, like, any sort of documentation that sort of comments in code meant to document. It could be the whole modular class. It could be on a per-method level, things like RDoc or YARD docs on Ruby classes. You used the word API docs here. I think that's a pretty similar idea. STEPHANIE: I really haven't given the idea of writing this kind of documentation a lot of thought because I've never had to do too much of it before, but I know, recently, you have been diving deep into it because, you know, like you said, you found these classes that you were working with a bit ambiguous, I suppose, or just confusing. And I'm wondering what kind of came out of that journey. What are some of the most interesting aspects of doing this exercise? JOËL: And one of the big ones, and it's not a fun one, but it is time-consuming. Writing good docs per method for a couple of classes takes a lot of time, and I understand why people don't do it all the time. STEPHANIE: What kinds of things were you finding warranted that time? Like, you know, you had to, at some point, decide, like, whether or not you're going to document any particular method. And what were some of the things you were looking out for as good reasons to do it? JOËL: I was making the decisions to document or not document on a class level, and then every public method gets documentation. If there's a big public API, that means every single one of those methods is getting some documentation comments, explaining what they do, how they're meant to be used, things like that. I think my kind of conclusion, having worked with this, is that the sort of sweet spot for this sort of documentation is for anything that is library-like, so a lot of things that maybe would go into a Rails lib directory might make sense. Anything you're turning into a gem that probably makes sense. And sometimes you have things in your Rails codebase that are effectively kind of library-like, and that was the case for the code that I was dealing with. It was almost like a mini ORM style kind of ActiveRecord-inspired series of base classes that had a bunch of metaprogramming to allow you to write models that were backed by not a database but a headless CMS, a content management system. And so, these classes are not extracted to the lib directory or, like, made into a gem, but they feel very library-esque in that way. STEPHANIE: Library-like; I like that descriptor a lot because it immediately made me think of another example of a time when I've used or at least, like, consumed this type of documentation in a, like, SaaS repo. Rather, you know, I'm not really seeing that level of documentation around domain objects, but I noticed that they really did a lot of extending of the application record class because they just had some performance needs that they needed to write some, like, custom code to handle. And so, they ended up kind of writing a lot of their own ORM-like methods for just some, like, custom callbacks on persisting and some just, like, bulk insertion functionality. And those came with a lot of different ways to use them. And I really appreciated that they were heavily documented, kind of like you would expect those ActiveRecord methods to be as well. JOËL: So, I've been having some conversations with other members at thoughtbot about when they like to use the style of module doc. What are some of the alternatives? And one that kept coming up for different people that they would contrast with this is what they would call the big README approach, and this could be for a whole gem, or it could be maybe some directory with a few classes in your application that's got a README in the root of the directory. And instead of documenting each method, you just write a giant README trying to answer sort of all of the questions that you anticipate people will ask. Is that something that you've seen, and how do you feel about that as a tool when you're looking for help? STEPHANIE: Yes. I actually really like that style of documentation. I find that I just want examples to get me started, especially; I guess this is especially true for libraries that I'm not super familiar with but need to just get a working knowledge about kind of immediately. So, I like to see examples, the getting started, the just, like, here's what you need to know. And as I start to use them, that will get me rolling. But then, if I find I need more details, then I will try to seek out more specific information that might come in the form of class method documentation. But I'm actually thinking about how FactoryBot has one of the best big README-esque [laughs] style of documentation, and I think they did a really big refresh of the docs not too long ago. It has all that high-level stuff, and then it has more specific information on how to use, you know, the most common methods to construct your factories. But those are very detailed, and yet they do sit, like, separately from inline, like, code documentation in the style of module docs that we're talking about. So, it is kind of an interesting mix of both that I think is helpful for me personally when I want both the “what do I need to know now?” And the, “like, okay, I know where to look for if I need something a little more detailed.” JOËL: Yeah. The two don't need to be mutually exclusive. I thought it was interesting that you mentioned how much examples are valuable to you because...I don't know if this is controversial, but an opinion that I have about sort of per-method documentation is that you should always default to having a code example for every method. I don't care how simple it is or how obvious it is what it does. Show me a code example because, as a developer, examples are really, really helpful. And so, seeing that makes documentation a lot more valuable than just a couple of lines that explain something that was maybe already obvious from the title of the method. I want to see it in action. STEPHANIE: Interesting. Do you want to see it where the method definition is? JOËL: Yes. Because sometimes the method definition, like, the implementation, might be sort of complex. And so, just seeing a couple of examples, like, oh, you call with this input, you get that. Call with this other input; you get this other thing. And we see this in, you know, some of the core docs for things like the enumerable methods where having an example there to be like, oh, so that's how map works. It returns this thing under these circumstances. That sort of thing is really helpful. And then, I'll try to do it at a sort of a bigger level for that class itself. You have a whole paragraph about here's the purpose of the class. Here's how you should use it. And then, here's an example of how you might use it. Particularly, if this is some sort of, like, base class you're meant to inherit from, here's the circumstances you would want to subclass this, and then here's the methods you would likely want to override. And maybe here are the DSLs you might want to have and to kind of package that in, like, a little example of, in this case, if you wanted a model that read from the headless CMS, here's what an example of such a little model might look like. So, it's kind of that putting it all together, which I think is nice in the module docs. It could probably also live in the big README at some level. STEPHANIE: Yeah. As you are saying that, I also thought about how I usually go search for tests to find examples of usage, but I tend to get really overwhelmed when I see inline, like, that much inline documentation. I have to, like, either actively ignore it, choose to ignore it, or be like, okay, I'm reading this now [laughs]. Because it just takes up so much visual space, honestly. And I know you put a lot of work into it, a lot of time, but maybe it's because of the color of my editor theme where comments are just that, like, light gray [laughs]. I find them quite easy to just ignore. But I'm sure there will be some time where I'm like, okay, like, if I need them, I know they're there. JOËL: Yeah, that is, I think, a downside, right? It makes it harder to browse the code sometimes because maybe your entire screen is almost taken up by documentation, and then, you know, you have one method up, and you've got to, like, scroll through another page of documentation before you hit the next method, and that makes it harder to browse. And maybe that's something that plays into the idea of that separation between library-esque code versus application code. When you browse library-esque code, when you're actually browsing the source, you're probably doing it for different reasons than you would for code in your application because, at that point, you're effectively source diving, sometimes being like, oh, I know this class probably has a method that will do the thing I want. Where is it? Or you're like, there's an edge case I don't understand on this method. I wonder what it does. Let me look at the implementation. Or even some existing code in the app is using this library method. I don't know what it does, but they call this method, and I can't figure out why they're using it. Let me look at the source of the library and see what it does under the hood. STEPHANIE: Yeah. I like the distinction of it is kind of a different mindset that you're reading the code at, where, like, sometimes my brain is already ready to just read code and try to figure out inputs and outputs that way. And other times, I'm like, oh, like, I actually can't parse this right now [chuckles]. Like, I want to read just English, like, telling me what to expect or, like, what to look out for, especially when, like you said, I'm not really, like, trying to figure out some strange bug that would lead me to diving deep in the source code. It's I'm at the level where I'm just reaching for a method and wanting to use it. We're writing these YARD docs. I think I also heard you mention that you gave some, like, tips or maybe some gotchas about how to use certain methods. I'm curious why that couldn't have been captured in a more, like, self-documenting way. Or was there a way that you could have written the code for that not to have been needed as a comment or documented as that? And was there a way that method names could have been clear to signal, like, the intention that you were trying to convey through your documentation? JOËL: I'm a big fan of using method names as a form of documentation, but they're frequently not good enough. And I think comments, whether they're just regular inline comments or more official documentation, can be really good to help avoid sort of common pitfalls. And one that I was working with was, there were two methods, and one would find by a UID, so it would search up a document by UID. And another one would search by ID. And when I was attempting to use these before I even started documenting, I used the wrong one, and it took me a while to realize, oh wait, these things have both UIDs and IDs, and they're slightly different, and sometimes you want to use one or the other. The method names, you know, said like, "Find by ID" or "Find by UID." I didn't realize there were both at the time because I wasn't browsing the source. I was just seeing a place where someone had used it. And then, when I did find it in the source, I'm like, well, what is the difference? And so, something that I did when I wrote the docs was sort of call out on both of those methods; by the way, there is also find by UID. If you're searching by UID, consider using the other one. If you don't know what the difference is, here's a sentence summarizing the difference. And then, here's a link to external documentation if you want to dive into the nitty gritty of why there are two and what the differences are. And I think that's something you can't capture in just a method name. STEPHANIE: Yeah, that's true. I like that a lot. Another use case you can think of is when method names are aliased, and it's like, I don't know how I would have possibly known that until I, you know, go through the journey of realizing [laughs] that these two methods do the same thing or, like, stumbling upon where the aliasing happens. But if that were captured in, like, a little note when I'm in, like, a documentation viewer or something, it's just kind of, like, a little tidbit of knowledge [laughs] that I get to gain along the way that ends up, you know, being useful later because I will have just kind of...I will likely remember having seen something like that. And I can at least start my search with a little bit more context than when you don't know what you don't know. JOËL: I put a lot of those sorts of notes on different methods. A lot of them are probably based on a personal story where I made a mistaken assumption about this method, and then it burned me. But I'm like, okay, nobody else is going to make that mistake. By the way, if you think this is what the method does, it does something slightly different and, you know, here's why you need to know that. STEPHANIE: Yeah, you're just looking out for other devs. JOËL: And, you know, trying to, like, take my maybe negative experience and saying like, "How can I get value out of that?" Maybe it doesn't feel great that I lost an hour to something weird about a method. But now that I have spent that hour, can I get value out of it? Is the sort of perspective I try to have on that. So, you mentioned kind of offhand earlier the idea of a documentation viewer, which would be separate than just reading these, I guess, code comments directly in your code editor. What sort of documentation viewers do you like to use? STEPHANIE: I mostly search in my browser, you know, just the official documentation websites for Rails, at least. And then I know that there are also various options for Ruby as well. And I think I had mentioned it before but using DuckDuckGo as my search engine. I have nice bang commands that will just take me straight to the search for those websites, which is really nice. Though, I have paired with people before who used various, like, macOS applications to do something similar. I think Alfred might have some built-in workflows for that. And then, a former co-worker used to use one called Dash, that I have seen before, too. So, it's another one of those just handy just, like, search productivity extensions. JOËL: You mentioned the Rails documentation, and this is separate from the guides. But the actual Rails docs are generated from comments like this inline in code. So, all the different ActiveRecord methods, when you search on the Rails documentation you're like, oh yeah, how does find_by work? And they've got a whole, like, paragraph explaining how it works with a couple of examples. That's this kind of documentation. If you open up that particular file in the source code, you'll find the comments. And it makes sense for Rails because Rails is more of, you know, library-esque code. And you and I search these docs pretty frequently, although we don't tend to do it, like, by opening the Rails gem and, like, grepping through the source to find the code comment. We do it through either a documentation site that's been compiled from that source or that documentation that's been extracted into an offline tool, like you'd mentioned, Dash. STEPHANIE: Yeah, I realized how conflicting, I suppose, it is for me to say that I find inline documentation really overwhelming or visually distracting, whereas I recognize that the only reason I can have that nice, you know, viewing experience is because documentation viewers use the code comments in that format to be generated. JOËL: I wonder if there's like a sort of...I don't know what this pattern is called, but a bit of a, like, middle-quality trap where if you're going to source dive, like, you'd rather just look at the code and not have too much clutter from sort of mediocre comments. But if the documentation is really good and you have the tooling to read it, then you don't even need to source dive at all. You can just read the documentation, and that's sufficient. So, both extremes are good, but that sort of middle kind of one foot in each camp is sort of the worst of both worlds experience. Because I assume when you look for Rails documentation, you never open up the actual codebase to search. The documentation is good enough that you don't even need to look at the files with the comments and the code. STEPHANIE: Yeah, and I'm just recalling now there's, like, a UI feature to view the source from the documentation viewer page. JOËL: Yes. STEPHANIE: I use that actually quite a bit if the comments are a little bit sparse and I need just the code to supplement my understanding, and that is really nice. But you're right, like, I very rarely would be source diving, unless it's a last resort [laughs], let's be honest. JOËL: So, we've talked about documentation viewers and how that can make things nice, and you're able to read documentation for things. But a lot of other tooling can benefit from this sort of model documentation as well, and I'm thinking, in particular, Solargraph, which is Ruby's language server protocol. And it has plugins for VS Code, for Vim, for a few different editors, takes advantage of that to provide all sorts of things. So, you can get smart expansion of code and good suggestions. You can get documentation for what's under your cursor. Maybe you're reading somebody else's code that they've written, and you're like, why are they calling this parameterized method here? What does that even do? Like, in VS Code, you could just hover over it, and it will pop up and show you documentation, including the, like, inputs and return types, and things like that. That's pretty nifty. STEPHANIE: Yeah, that is cool. I use VS Code, but I've not seen that too much yet because I don't think I've worked in enough codebases with really comprehensive [laughs] YARD docs. I'm actually wondering, tooling-wise, did you use any helpful tools when you were writing them or were you hand-documenting each? JOËL: I was hand-documenting everything. STEPHANIE: Class. Okay. JOËL: The thing that I did use is the YARD gem, which you don't need to have the gem to write YARD-style documentation. But if you have the gem, you can run a local server and then preview a documentation site that is generated from your comments that has everything in there. And that was incredibly helpful for me as I was trying to sort of see an overview of, okay, what would someone who's looking at the docs generated from this see when they're trying to look for what the documentation of a particular method does? STEPHANIE: Yeah, and that's really nice. JOËL: Something that I am curious about that I've not really had a lot of experience with is whether or not having extra documentation like that can help AI tools give us better suggestions. STEPHANIE: Yeah, I don't know the answer to that either, but I would be really curious to know if that is already something that happens with something like Copilot. JOËL: Do better docs help machines, or are they for humans only? STEPHANIE: Whoa, that's a very [laughs] philosophical question, I think. It would make sense, though, that if we already have ways to parse and compile this kind of documentation, then I can see that incorporating them into the types of, like, generative problems that AI quote, unquote "solves" [chuckles] would be really interesting to find out. But anyone listening who kind of knows the answer to that or has experience working with AI tools and various types of code comment documentation would be really curious to know what your experience is like and if it improves your development workflow. So, for people who might be interested in getting better at documenting their code in the style of module docs, what would you say are some really great attributes of good documentation in this form? JOËL: I think, first of all, you have to write from the motivation of, like, if you were confused and wanting to better understand what a method does, what would you like to see? And I think coming from that perspective, and that was, in my case, I had been that person, and then I was like, okay, now that I've figured it out, I'm going to write it so that the next person is not confused. I have five or six things that I think were really valuable to add to the docs, a few of which we've already mentioned. But rapid fire, first of all, code example. I love code examples. I want a code example on every method. An explanation of expected usage. Here's what the method does. Here's how we expect you to use this method in any extra context about sort of intended use. Callouts for suggested alternatives. If there are methods that are similar, or there's maybe a sort of common mistake that you would reach for this method, put some sort of call out to say, "Hey, you probably came here trying to do X. If that's what you were actually trying to do, you should use method Y." Beyond that, a discussion of edge cases, so any sort of weird ways the method behaves. You know, when you pass nil to it, does it behave differently? If you call it in a different context, does it behave differently? I want to know that so that I'm not totally surprised. Links to external resources–really great if I want to, like, dig deeper. Is this method built on some sort of, like, algorithm that's documented elsewhere? Please link to that algorithm. Is this method integrating with some, like, third-party API? You know, they have some documentation that we could link to to go deeper into, like, what these search options do. Link to that. External links are great. I could probably find it by Googling myself, but you are going to make me very happy as a developer if you already give me the link. You'd mentioned capturing inputs and outputs. That's a great thing to scan for. Inputs and outputs, though, are more sometimes than just the arguments and return values. Although if we're talking about arguments, any sort of options hash, please document the keys that go in that because that's often not obvious from the code. And I've spent a lot of time source diving and jumping between methods trying to figure out like, what are the options I can pass to this hash? Beyond the explicit inputs and outputs, though, anything that is global state that you rely on. So, do you need to read something from an environment variable or even a global variable or something like that that might make this method behave differently in different situations? Please document that. Any situations where you might raise an error that I might not expect or that I might want to rescue from, let me know what are the potential errors that might get raised. And then, finally, any sorts of side effects. Does this method make a network call? Are you writing to the file system? I'd like to know that, and I'd have to, like, figure it out by trial and error. And sometimes, it will be obvious in just the description of the method, right? Oh, this method pulls data from a third-party API. That's pretty clear. But maybe it does some sort of, like, caching in the background or something to a file that's not really important. But maybe I'm trying to do a unit test that involves this, and now, all of a sudden, I have to do some weird stubbing. I'd like to know that upfront. So, those are kind of all the things I would love to have in my sort of ideal documentation comment that would make my life easier as a developer when trying to use some code. STEPHANIE: Wow. What a passionate plea [laughs]. I was very into listening to you list all of that. You got very animated. And it makes a lot of sense because I feel like these are kind of just the day-to-day developer issues we run into in our work and would be so awesome if, especially as the, you know, author where you have figured all of this stuff out, the author of a, you know, a method or a class, to just kind of tell us these things so we don't have to figure it out ourselves. I guess I also have to respond to that by saying, on one hand, I totally get, like, you want to be saved [chuckles] from those common pitfalls. But I think that part of our work is just going through that and playing around and exploring with the code in front of us, and we learn all of that along the way. And, ultimately, even if that is all provided to you, there is something about, like, going through it yourself that gives you a different perspective on it. And, I don't know, maybe it's just my bias against [laughs] all the inline text, but I've also seen a lot of that type of information captured at different levels of documentation. So, maybe it is a Confluence doc or in a wiki talking about, you know, common gotchas for this particular problem that they were trying to solve. And I think what's really cool is that, you know, everyone can kind of be served and that people have different needs that different styles of documentation can meet. So, for anyone diving deep in the source code, they can see all of those examples inline. But, for me, as a big Googler [laughs], I want to see just a nice, little web app to get me the information that I need to find. I'm happy having that a little bit more, like, extracted from my source code. JOËL: Right. You don't want to have to read the source code with all the comments in it. I think that's a fair criticism and, yeah, probably a downside of this. And I'm wondering, there might be some editor tooling that allows you to just collapse all comments and hide them if you wanted to focus on just the code. STEPHANIE: Yeah, someone, please build that for me. That's my passionate plea [laughs]. And on that note, shall we wrap up? JOËL: Let's wrap up. STEPHANIE: Show notes for this episode can be found at bikeshed.fm. JOËL: This show has been produced and edited by Mandy Moore. STEPHANIE: If you enjoyed listening, one really easy way to support the show is to leave us a quick rating or even a review in iTunes. It really helps other folks find the show. JOËL: If you have any feedback for this or any of our other episodes, you can reach us @_bikeshed, or you can reach me @joelquen on Twitter. STEPHANIE: Or reach both of us at hosts@bikeshed.fm via email. JOËL: Thanks so much for listening to The Bike Shed, and we'll see you next week. ALL: Bye. AD: Did you know thoughtbot has a referral program? If you introduce us to someone looking for a design or development partner, we will compensate you if they decide to work with us. More info on our website at: tbot.io/referral. Or you can email us at referrals@thoughtbot.com with any questions.

    416: Multi-Dimensional Numbers

    Play Episode Listen Later Feb 27, 2024 39:31


    Joël discusses the challenges he encountered while optimizing slow SQL queries in a non-Rails application. Stephanie shares her experience with canary deploys in a Rails upgrade. Together, Stephanie and Joël address a listener's question about replacing the wkhtml2pdf tool, which is no longer maintained. The episode's main topic revolves around the concept of multidimensional numbers and their applications in software development. Joël introduces the idea of treating objects containing multiple numbers as single entities, using the example of 2D points in space to illustrate how custom classes can define mathematical operations like addition and subtraction for complex data types. They explore how this approach can simplify operations on data structures, such as inventories of T-shirt sizes, by treating them as mathematical objects. EXPLAIN ANALYZE visualizer (https://explain.dalibo.com/) Canary in a coal mine (https://en.wikipedia.org/wiki/Sentinel_species#Canaries) Episode 413: Developer Tales of Package Management (https://bikeshed.thoughtbot.com/413) Docs for media-specific CSS (https://developer.mozilla.org/en-US/docs/Web/CSS/@media) Episode 386: Value Objects Revisited: The Tally Edition (https://bikeshed.thoughtbot.com/386) Money gem (https://github.com/RubyMoney/money) Transcript: STEPHANIE: Hello and welcome to another episode of The Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I'm Stephanie Minn. JOËL: And I'm Joël Quenneville. And together, we're here to share a bit of what we've learned along the way. STEPHANIE: So, Joël, what's new in your world? JOËL: I've recently been trying to do some performance enhancements to some very slow queries. This isn't a Rails app, so we're sort of combining together a bunch of different scopes. And the way they're composing together is turning out to be really slow. And I've reached for a tool that is just really fun. It's a visualizer for SQL query plans. You can put the SQL keywords in front of a query: 'EXPLAIN ANALYZE,' and it will then output a query plan, sort of how it's going to attempt to do the work. And that might be like, oh, we're going to use this index on this table to join on this other thing, and then we're going to...maybe this is a table that we think we're going to do a sequential scan through and, you know, it builds out a whole thing. It's a big block of text, and it's kind of intimidating to look at. So, there are a few websites out there that will do this. You just paste a query plan in, and they will build you a nice, little visualization, almost like a tree of, like, tasks to be done. Oftentimes, they'll also annotate it with metadata that they pulled from the query plan. So, oh, this particular node is the really expensive one because we're doing a sequential scan of this table that has 15 million rows in it. And so, it's really useful to then sort of pinpoint what are the areas that you could optimize. STEPHANIE: Nice. I have known that you could do that EXPLAIN ANALYZE on a SQL query, but I've never had to do it before. Is this your first time, or is it just your first time using the visualizer? JOËL: I've played around with EXPLAIN ANALYZE a little bit before. Pro tip: In Rails, if you've got a scope, you can just chain dot explain on the end, and instead of running the query, it will run the EXPLAIN version of it and return the query plan. So, you don't need to, like, turn into SQL then manually run it in your database system to get the EXPLAIN. You can just tack a dot explain on there to get the query plan. It's still kind of intimidating, especially if you've got a really complex query that's...this thing might be 50 lines long of EXPLAIN with all this indentation and other stuff. So, putting it into a sort of online visualizer was really helpful for the work that I was doing. So, it was my first time using an online visualizer. There are a few out there. I'll link to the one that I used in the show notes. But I would do that again, would recommend. STEPHANIE: Nice. JOËL: So, Stephanie, what's new in your world? STEPHANIE: So, I actually just stepped away from being in the middle of doing a Rails upgrade [chuckles] and releasing it to production just a few minutes before getting on to record with you on this podcast. And the reason I was able to do that, you know, without feeling like I had to just monitor to see how it was going is because I'm on a project where the client is using canary deploys. And I was so pleasantly surprised by how easy it made this experience where we had decided to send the canary release earlier this morning. And the way that they have it set up is that the canary goes to 10% of traffic. 10% of the users were on Rails 7 for their sessions. And we saw a couple of errors in our error monitoring service. And we are like, "Okay, like, let's take a look at this, see what's going on." And it turns out it was not too big of a deal because it had to do with, like, a specific page. And, for the most part, if a user did encounter this error, they probably wouldn't again after refreshing because they had, like, a 90% chance [chuckles] of being directed to the previous version where everything is working. And we were kind of making that trade-off of like, oh, we could hotfix this right now on the canary release. But then, as we were starting to debug a little bit, it was a bit hairier than we expected originally. And so, you know, I said, "I have to hop on to go record The Bike Shed. So, why don't we just take this canary down just for the time being to take that time pressure off? And it's Friday, so we're heading into the weekend. And maybe we can revisit the issue with some fresh eyes." So, I'm feeling really good, actually. And I'm glad that we were able to do something that seems scary, but there were guardrails in place to make it a lot more chill. JOËL: Yay for the ability to roll back. You used the term canary release. That's not one that I'm familiar with. Can you explain what a canary release is? STEPHANIE: Oh yeah. Have you heard of the phrase 'Canary in the coal mine'? JOËL: I have. STEPHANIE: Okay. So, I believe it's the same idea where you are, in this case, releasing a potentially risky change, but you don't want to immediately make it available to, like, all of your users. And so, you send this change to, like, a small reach, I suppose, and give it a little bit of a test and see [chuckles] what comes back. And that can help inform you of any issues or risks that might happen before kind of committing to deploying a potentially risky change with a bigger impact. JOËL: Is this handled with something like a feature flag framework? Or is this, like, at an infrastructure level where you're just like, "Hey, we've got the canary image in, like, one container on one server, and then we'll redirect 10% of traffic to that to be served by that one and the other 90% to be served by the old container or something like that"? STEPHANIE: Yeah, in this case, it was at the infrastructure level. And I have also seen something similar at a feature flag level, too, where you're able to have some more granularity around what percent of users are seeing a feature. But I think with something like a Rails upgrade, it was nice to be able to have that at that infrastructure level. It's not necessarily, like, a particular page or feature to show or not show. JOËL: Yeah, I think you would probably want that at a higher level when you're changing over the entire app. Is this something that you had to custom-build yourself or something that just sort of came out of the box with some of the infrastructure tools you're using? STEPHANIE: It came out of the box, actually. I just joined this client project this week and was very delighted to see just some really great deployment infrastructure and getting to meet the DevOps engineers, too, who built it. And they're really proud of it. They kind of walked us through our first release earlier this week. And he was telling me, the DevOps engineer, that this was actually his favorite part of the job, is walking people through their first release and being their buddy while they do it. Because I think he gets to also see users interact with the tool that he built, and he had a lot of pride in that, so it was a very delightful experience. JOËL: That's so wonderful. I've been on so many projects where the sort of infrastructure side of things is not the team's strong point, and releasing can be really scary. And it's great to hear the opposite of that. We recently received a question for Stephanie based on an earlier episode. So, the question asks, "In episode 413, Stephanie discussed a recent issue she encountered with wkhtml2pdf. The episode turned into a deeper discussion about package management, but I don't think it ever cycled back to the conclusion. I'm curious: how did Stephanie solve this dilemma? We're facing the same issue on a project that my team maintains. It's an old codebase, and there are bits of old code that use wkhtml2pdf to generate print views of our data in our application. The situation is fairly dire. wkhtml2pdf is no longer maintained. In fact, it won't even be available to install from our operating system's package repositories in June. We're on FreeBSD, but I assume the same will be eventually true for other operating systems. And so, unless you want to maintain some build step to check out and compile the source code for an application that will no longer receive security updates, just living with it isn't really an option. There are three options we're considering. One, eliminate the dependency entirely. Based on user feedback, it sounds like our old developers were using this library to generate PDFs when what users really wanted was an easy way to print. So, instead of downloading a PDF, just ensure the screen has a good print style sheet and register an onload handler to call window dot print. We're thinking we could implement this as an A/B test to the feature to test this theory. Or two, replace wkhtml2pdf with a call to Headless Chrome and use that to generate the PDF. Or, three, replace wkhtml2pdf with a language-level package. For us, that might be the dompdf library available via Composer because we're a PHP shop." Yeah, a lot to unpack here. Any high-level thoughts, Stephanie? STEPHANIE: My first thought while I was listening to you read that question is that wkhtml2pdf is such a mouthful [laughs]. And I was impressed how you managed to say it at least, like, five times. JOËL: So, I try to say that five times fast. STEPHANIE: And then, my second high-level thought was, I'm so sorry to Brian, our listener who wrote in, because I did not really solve this dilemma [chuckles] for my project and team. I kind of kicked the can down the road, and that's because this was during a support and maintenance rotation that I've talked a little bit about before on the show. I was only working on this project for about a week. And what we thought was a small bug to figure out why PDFs were a little bit broken turned out, as you mentioned, to be this kind of big, dire dilemma where I did not feel like I had enough information to make a good call about what to do. So, I kind of just shared my findings that, like, hey, there is kind of a risk and hoping that someone else [laughs] would be able to make a better determination. But I really was struck by the options that you were considering because it was actually a bit of a similar situation to the bug I was sharing where the PDF that was being generated that was slightly broken. I don't think it was, like, super valuable to our users that it be in the form of a PDF. It really was just a way for them to print something to have on handy as a reference from, you know, some data that was generated from the app. So, yeah, based on what you're sharing, I feel really excited about the first one. Joël, I'm sure you have some opinions about this as well. JOËL: I love sort of the bigger picture thinking that Brian is doing here, sort of stepping back and being like, wait, why do we even need PDF here, and how are our customers using it? I think those are the really good questions to ask before sinking a ton of time into coming up with something that might be, like, a bit of a technical wonder. Like, hey, we managed to, like, do this PDF generation thing that we had to, like, cobble together so many other things. And it's so cool technically, but does it actually solve the underlying problem? So, shout out to Brian for thinking about it in those terms. I love that. Second cool thing that I wanted to shout out, because I think this is a feature of browsers that not many people are aware of; you can have multiple style sheets for your page, and you can tag them to be for different media. So, you can have a style sheet that only gets applied when you print versus when you display on screen. And there are a couple of others. I don't remember exactly what they are. I'll link to the docs in the show notes. But taking advantage of this, like, this is old technology but making that available and saying, "Yeah, we'll make it so that it's nice when you print, and we'll maybe even, you know, a link or a button with JavaScript so that you could just Command-P or Control-P to print. But we'll have a button in there as well that will allow you to print to PDF," and that solves your problem right there. STEPHANIE: Yeah, that's really cool. I didn't know that about being able to tag style sheets for different media types. That's really fascinating. And I like that, yeah, we're just eliminating this dependency on something, like, potentially really complex with a, hopefully, kind of elegant and modern solution, maybe. JOËL: And your browser is already able to do so many of these things. Why do we sort of try to recreate it? Printing is a thing browsers have been able to do for a long time. Printing to PDF is a thing that you can do for a long time. I will sometimes use that on sites where I need to, let's say I'm purchasing something, and I need some sort of receipt to expense, but they won't give me a download, a PDF download that I can send to the accounting team, so I will print to PDF the, like, HTML view. And that works just fine. It's kind of a workaround hack. Sometimes, it doesn't work well because the HTML page is just not well set up to, like, show up on a PDF page. You get some, like, weird, like, pagination issues or things like that. But, you know, just a little bit of thought for a print style sheet, especially for something you know that people are likely going to want to print or to save to PDF, that's a nice touch. STEPHANIE: Yeah. So, good luck, Brian, and let us know how this goes and any outcomes you find successful. So, for today's longer topic, I was excited because I saw, Joël, you dropped something in our topic backlog: Multidimensional Numbers. I'm curious what prompted this idea and what you wanted to say about it. JOËL: We did an episode a while back where we talked about value objects, wrapping numbers, wrapping collections. This is Episode 386, and we were talking about tallying, specifically working with collections of T-shirt sizes and doing math on these sort of objects that might contain multiple numbers. And a sort of sidebar from that that we didn't really get into is the idea that objects that contain sort of multiple numbers can be treated as a number themselves. And I think a great example of this is something like a point in two-dimensional space. It's got an x coordinate, a y coordinate. It's two numbers, but you can treat sort of the combination of the two of them together as a single number. There's a whole set of coordinate math that you can do to do things like add coordinates together, subtract them, find the distance between them. There's a whole field of vector math that we can do on those. And I think learning to recognize that numbers are not just instances of the integer or the float class but that there could be these more complex things that are also numbers is maybe an important realization and something that, as developers, if we think of these sort of more complex values as numbers, or at least mathematical objects, then that will help us write better code. STEPHANIE: Cool. Yeah. When you were first talking about 2D points, I was thinking about if I have experience working with that before or, like, having to build something really heavily based off of, like, a canvas or, you know, a coordinate system. And I couldn't think of any really good examples until I thought about, like, geographic locations. JOËL: Oh yeah, like a latitude, longitude. STEPHANIE: Yeah, exactly. Like, that is a lot more common, I think, for various types of just, like, production applications than 2D points if you're not working on, like, a video game or something like that, I think. JOËL: Right, right. I think you're much more likely to be working with 2D points on some more sort of front-end-heavy application. I was talking with someone this week about managing a seat map for concerts and events like that and sort of creating a seat map and have it be really interactive, and you can, like, click on seats and things like that. And depending on the level of libraries you're using to build that, you may have to do a lot of 2D math to make it all come together. STEPHANIE: Yeah. So, I would love to get into, you know, maybe we've realized, okay, we have some kind of compound number. What are some good reasons for using them differently than you would a primitive? JOËL: So, you mentioned primitives, and I think this is where maybe I'm developing a reputation about, like, always wanting value objects for everything. But it would be really easy, let's say, for an xy point to be just an array of two numbers or maybe even a hash with an x key and a y key. What's tricky about that is that then you don't have the ability to do math on them. Arrays do define the plus operator, but they don't do what you want them to do with points. It's the set union. So, adding two points would not at all do what you want, or subtracting two points. So, instead, if you have a custom 2D point class and you can define plus and minus on there to do the right thing, now they're not pairs of numbers, two values; they're a single value, and you can treat them as if they are just a single number. STEPHANIE: You mentioned that arrays don't do the right thing when you try to add them up. What is the right thing that you're thinking of then? JOËL: It probably depends a little bit on the type of object you're working with. So, with 2D points, you're probably trying to do vector addition where you're effectively saying almost, like, "Shift this point in 2D space by the amount of this other point." Or if you're doing a subtraction, you might even be asking, like, "What is the distance between these two points?" Euclidean distance, I think, is the technical term for this. There's also a couple of different ways you can multiply values. You can multiply a 2D point by just a sort of, not by another point, but by just an integer. That's called scaling. So, you're just like, oh, take this point in 2D space, but make it bigger, make it five times bigger or five times further from the origin. Or you can do some stuff with other points. But what you don't want to do is turn this into, if you're starting with arrays, you don't want to turn this into an array of four points. When you add two points in 2D space, you're not trying to create a point in 4D space. STEPHANIE: Whoa, I mean [laughs], maybe you're not. JOËL: You could but -- [laughter] STEPHANIE: Yeah. While you were saying that, I guess that is what is really cool about wrapping, encapsulating them in objects is that you get to decide what that means for you and your application, and -- JOËL: Yeah. Well, plus can mean different things, right? STEPHANIE: Yeah. JOËL: On arrays, plus means combining two arrays together. On integers, it means you do integer math. And on points, it might be vector addition. STEPHANIE: Are there any other arithmetic operators you can think of that would be useful to implement if you were trying to create some functionality on a point? JOËL: That's a good question because I think realizing the inverse of that is also a really powerful thing. Just because you create a sort of new mathematical object, a point in 2D space, doesn't mean that necessarily every arithmetic operator makes sense on it. Does it make sense to divide a point by another point? Maybe not. And so, instead of going with the mindset of, oh, a point is a mathematical object, I now need to implement all of arithmetic on this, instead, think in terms of your domain. What are the operations that make sense? What are the operations you need for this point? And, you know, maybe the answer is look up what are the common sort of vector math operations and implement those on your 2D point. Some of them will map to arithmetic operators like plus and minus, and then some of them might just be some sort of custom method where maybe you say, "Oh, I want the Euclidean distance between these two points." That's just a thing. Maybe it's just a named instance method on there. But yeah, don't feel like you need to implement all of the math operators because that's a mistake that I have made and then have ended up, like, implementing nonsensical things. STEPHANIE: [laughs] Creating your own math. JOËL: Yes, creating my own math. I've done this even on where I've done value objects to wrap single values. I was doing a class to represent currency, and I was like, well, clearly, you need, like, methods to, like, add or subtract your currency, and that's another thing. If you have, let's say, a plus method, now you can plug it into, let's say, reduce plus. And you can just sum a list of these currency objects and get back a new currency. It's not even going to give you back an integer. You just get a sort of new currency object that is the sum of all the other ones, and that's really nice. STEPHANIE: Yeah, that's really cool. It reminds me of all the magic of enumerable that you had talked about in a previous conference talk, where, you know, you just get so much out of implementing those basic operators that, like, kind of scales in handiness. JOËL: Yes. Turns out Ruby is actually a pretty nice system. If you have objects that respond to some common methods and you plug them into enumerable, and it just all kind of works. STEPHANIE: So, one thing you had said earlier that I've felt kind of excited about and wanted to highlight was you mentioned all the different ways that you could represent a 2D point with more primitive data stores, so, you know, an array of two integers, a hash with xy keys. It got me thinking about how, yeah, like, maybe if your system has to talk to another system and you're importing data or exporting data, it might eventually need to take those forms. But what is cool about having an encapsulated object in your application is you can kind of control those boundaries a little bit and have more confidence in terms of the data types that you're using within your system by having various ways to construct that, like, domain object, even if the data coming in is in a different shape. JOËL: And I think that you're hitting on one of the real beauties of object-oriented programming, where the sort of users of your object don't need to know about the internal representation. Maybe you store an array internally. Maybe it's two separate instance variables. Maybe it's something else entirely. But all that the users of your, let's say, 2D point object really need to care about is, hey, the constructor wants values in this shape, and then I can call these domain methods on it, and then the rest just sort of happens. It's an implementation detail. It doesn't matter. And you alluded, I think, to the idea that you can sort of create multiple constructors. You called them constructors. I tend to call them that as well. But they're really just class methods that will kind of, like, add some sugar on top of the constructor. So, you might have, like, a from array pair or from hash or something like that that allows you to maybe do a little bit of massaging of the data before you pass it into your constructor that might want some underlying form. And I think that's a pattern that's really nice. STEPHANIE: Yeah, I agree. JOËL: Something that can be interesting there, too, is that mathematically, there are multiple ways you can think of a 2D point. An xy coordinate pair is a common one, but another sort of system for representing a point in 2D space is called the polar coordinate system. So, you have some sort of, like, origin point. You're 0,0. And then, instead of saying so many to the left and so many up from that origin point, you give an angle and a distance, and that's where your point is. So, an angle and distance point, I think, you know, theta and magnitude are the fancy terms for this. You could, instead of creating a separate, like, oh, I have a polar coordinate point and a Cartesian coordinate point, and those are separate things, you can say, no, I just have a point in 2D space. They can be constructed from either an xy coordinate pair or a magnitude angle pair. Internally, maybe you convert one to the other for internal representation because it makes the math easier or whatever. Your users never need to know that. They just pass in the values that they want, use the constructor that is most convenient for them, and it might be both. Maybe some parts of the app require polar coordinates; some require Cartesian coordinates. You could even construct one of each, and now you can do math with each other because they're just instances of the same class. STEPHANIE: Whoa. Yeah, I was trying to think about transforming between the two types as well. It's all possible [laughs]. JOËL: Yes. Because you could have reader-type methods on your object that say, oh, for this point, give me its x coordinate; give me its y coordinate. Give me its distance from the origin. Give me its angle from the origin. And those are all questions you can ask that object, and it can calculate them. And you don't need to care what its internal representation is to be able to get all four of those. So, we've been talking about a lot of these sort of composite numbers, not composite numbers, that's a separate mathematical thing, but numbers that are composed of sort of multiple sub-numbers. And what about situations where you have two things, and one of them is not a number? I'm thinking of all sorts of units of measure. So, I don't just have three. I have three, maybe...and we were talking about currency earlier, so maybe three U.S. dollars. Or I don't just have five; I have five, you know, let's say, meters of distance. Would you consider something like that to be one of these compound number things? STEPHANIE: Right. I think I was–when we were originally talking about this, conflating the two. But I realized that, you know, just because we're adding context to a number and potentially packaging it as a value object, it's still different from what we're talking about today where, you know, there's multiple components to the number that are integral or required for it to mean what we intended to mean, if that makes sense. JOËL: Yeah. STEPHANIE: So yeah, I guess we did want to kind of make a distinction between value objects that while the additional context is important and you can implement a lot of different functionality based on what it represents, at the end of the day, it only kind of has one magnitude or, like, one integer to kind of encapsulate it represented as a number. Does that sound right? JOËL: Yeah. You did throw out the words encapsulation and value object. So, in a situation maybe where I have three US dollars, would you create some kind of custom object to wrap that? Or is that a situation where you'd be more comfortable using some kind of primitive? Like, I don't know, maybe an array pair of three and the symbol USD or something like that. STEPHANIE: Oh, I would definitely not do that [laughter]. Yeah. Like I, you know, for the most part, I think I've seen that as a currency object, and that expands the world of what we can do with it, converting into a lot of different other currencies. And yeah, just making sure those things don't get divorced from each other because that context is what gives it meaning. But when it comes to our compound numbers, it's like, without all of the components, it doesn't make sense, or it doesn't even represent the same, like, numerical value that we were trying to convey. JOËL: Right. You need both, or, you know, it could be more than two. It could be three, four, or five numbers together to mean something. You mentioned conversions, which I think is something that's also interesting because a lot of units of measure have sort of multiple ways of measuring, and you often want to convert between them. And maybe that's another case where encapsulation is really nice where, you know, maybe you have a distance object. And you have five meters, and you put that into your distance object, but then somebody wants it in feet somewhere else or in centimeters, or something like that. And it can just do all the conversion math safely inside that object, and the user doesn't have to worry about it. STEPHANIE: Right. This is maybe a bit of a tangent, but as a Canadian living in the U.S., I don't know [laughs] if you have any opinions about converting meters and feet. JOËL: The one I actually do the most often is converting Celsius to Fahrenheit and vice versa. You know, I've been here, what, 11 years now? I don't have a great intuition for Fahrenheit temperatures. So, I'm converting in my head just [laughs] on a daily basis. STEPHANIE: Yeah, that makes sense. Conversions: they're important. They help out our friends who [laughs] are on different systems of measurement. JOËL: There's a classic story that I love about unit conversions. I think it's one of the NASA Mars missions. STEPHANIE: Oh yeah. JOËL: You've heard of this one. It was trying to land on Mars, and it burned up in the atmosphere because two different teams had been building different components and used different unit systems, both according to spec for their own module. But then, when the modules try to talk to each other, they're sending over numbers in meters instead of feet or something like that. And it just caused [laughs] this, like, multi-year, multi-billion dollar project to just burn up. STEPHANIE: That's right. So, lesson of the day is don't do that. I can think of another example where there might be a little bit of misconceptions in terms of how to represent it. And I'm thinking about time and when that has been represented in multiple parts, such as in hours and, minutes and seconds. Do you have any initial impressions about a piece of data like that? JOËL: So, that's really interesting, right? Because, at first glance, it looks like, oh, it's, like, a triplet of hour, minute, seconds. It's sort of another one of these sort of compound numbers, and I guess you could implement it that way. But in reality, you're tracking a single quantity, the amount of time elapsed, and that can be represented with a single number. So, if you're representing, let's say, time of day, what would show up on your clock? That could be, depending on the resolution, number of, let's say, seconds since midnight, and that's a single counter. And then, you can do some math on it to get hours, minutes, seconds for a particular moment. But really, it's a single quantity, and we can do that with time. We can't do that with a 2D point. Like, it has to have two components. STEPHANIE: So, do you have a recommendation for what unit of time time would best be stored? I'm just thinking of all the times that I've had to do that millisecond, you know, that conversion of, you know, however many thousands of milliseconds in my head into something that actually means [laughs] something to me as a human being who measures time in hours and minutes. JOËL: My recommendation is absolutely go for a single number that you store in your, let's say, time of day object. It makes the math so much easier. You don't have to worry about, like, overflowing from one number into another when you're doing math or anything like that. And then the number that you count should be at the whatever the smallest resolution you care at. So, is there ever any time where you want to distinguish between two different milliseconds in time? Or maybe you're like, you know what? These are, like, we're tracking time of day for appointments. We don't care about the difference between two milliseconds. We don't need to track them independently. We don't even care about seconds. The most granular we ever care about things is by the minute. And so, maybe then your internal number that you track is a counter of minutes since midnight. But if you need more precision, you can go down to seconds or milliseconds or nanoseconds. But yeah, find what is the sort of the least resolution you want to get away with and then make that the unit of measure for a single counter in your object. And then encapsulate that so that nobody else needs to care that, internally, your time of day object is doing milliseconds because nobody wants to do that math. Just give me a nice, like, hours and minutes method on your object, and I will use that. I don't need to know internally what it's using. Please don't just pass around integers; wrap it in an object, especially because integers, there's enough times where you're doing seconds versus milliseconds. And when I just have an integer, I never know if the person storing this integer means seconds or milliseconds. So, I'm just like, oh, I'm going to pass to this, like, user object, a, like, time integer. And unless there's a comment or a constant, you know, that's named something duration in milliseconds or something like that, you know, or sometimes even, like, one year in milliseconds, or there's no way of knowing. STEPHANIE: Yeah. That makes a lot of sense. When you kind of choose a standard of a standard unit, it's, like, possible to make it easier [laughs]. JOËL: So, circling back to sort of the initial thing that sparked this conversation, the previous episode about T-shirt inventories, there we were dealing with what started off as, like, a hash of different T-shirt sizes and quantities of T-shirts that we had in that size, so small (five), medium (three), large (four). And then, we eventually turned that into a value object that represented...I think we called it a tally, but maybe we called it inventory. And this may be wrong, so tell me if I'm wrong here, I think we can kind of treat that as a number, as, like, one of these compound numbers. It's a sort of multidimensional number where you say, well, we have sort of three dimensions where we can have numbers that sort of increase and decrease independently. We can do math on these because we can take inventories or tallies and add and subtract them. And that's what we ended up having to do. We created a value object. We implemented plus and minus on it. There are rules for how the math works. I think this is a multidimensional number with the definition we're working on this show. Am I wrong here? STEPHANIE: I wouldn't say that you're wrong. I think I would have to think a little [laughs] more to say definitively that you're right. But I know that this example came from, you know, an application I was actually working on. And one of the main things that we had to do with these representations [laughs], I'm hesitant to call them a number, especially, but we had to compare these representations frequently because an inventory, for example, in a warehouse, wanting to make sure that it is equal to or there's enough of the inventory if someone was placing an order, which would also contain, like, a representation of T-shirt size inventory. And that was kind of where some of that math happened because, you know, maybe we don't want to let someone place an order if the inventory at the warehouse is smaller than their order, right? So, there is something really compelling about the comparison operations that we were doing that kind of is leaning me in the direction of, like, yeah, like, it makes sense to me to use this in a way that I would compare, like, quantities or numbers of something. JOËL: I think one thing that was really compelling to me, and that kind of blew my mind, was that we were trying to, like, figure out some things like, oh, we've got so many people with these size preferences, and we've got so many T-shirts across different warehouses. And we're summing them up and we're trying to say like, "How many do we need to purchase if there is a deficit?" And we can come up with effectively a formula for this. We're like, sum these numbers, when we're talking about just before we introduce sizes when it's just like, oh, people have T-shirts. They all get the count of people and a count of T-shirts in our warehouse, and we find, you know, the difference between that. And there's a few extra math operations we do. Then you introduce size, and you break it down by, oh, we've got so many of each. And now the whole thing gets really kind of messy and complicated. And you're doing these reduces and everything. When we start treating the tally of T-shirts as an object, and now it's a number that responds to plus and minus, all of a sudden, you can just plug those back into the original formula, and it all just works. The original formula doesn't care whether the numbers you're doing this formula on are simple integers or these sort of multidimensional numbers. And that blew my mind, and it was so cool. STEPHANIE: Yeah, that is really neat. And you get a lot of added benefits, too. I think the other important piece in the T-shirt size example was kind of tracking the state change, and that's so much easier when you have an object. There's just a lot more you can do with it. And even if, you know, you're not persisting every single version of the representation, you know, because sometimes you don't want to, sometimes you're really just kind of only holding it in memory to figure out if you need to, you know, do something else. But other times, you do want to persist it. And it just plugs in really well with, like, the rest of object-oriented programming [laughs] in terms of interacting with the rest of your business needs, I think, in your app. JOËL: Yeah, turns out objects, they're kind of nice. And you can do math with them. Who knew? Math is not just about integers. STEPHANIE: And on that note, shall we wrap up? JOËL: Let's wrap up. STEPHANIE: Show notes for this episode can be found at bikeshed.fm. JOËL: This show has been produced and edited by Mandy Moore. STEPHANIE: If you enjoyed listening, one really easy way to support the show is to leave us a quick rating or even a review in iTunes. It really helps other folks find the show. JOËL: If you have any feedback for this or any of our other episodes, you can reach us @_bikeshed, or you can reach me @joelquen on Twitter. STEPHANIE: Or reach both of us at hosts@bikeshed.fm via email. JOËL: Thanks so much for listening to The Bike Shed, and we'll see you next week. ALL: Byeeeeeee!!!!!! AD: Did you know thoughtbot has a referral program? If you introduce us to someone looking for a design or development partner, we will compensate you if they decide to work with us. More info on our website at: tbot.io/referral. Or you can email us at referrals@thoughtbot.com with any questions.

    415: Codebase Calibration

    Play Episode Listen Later Feb 6, 2024 30:54


    Stephanie has a delightful and cute Ruby thing to share: Honeybadger, the error monitoring service, has created exceptionalcreatures.com, where they've illustrated and characterized various common Ruby errors into little monsters, and they're adorable. Meanwhile, Joël encourages folks to submit proposals for RailsConf. Together, Stephanie and Joël delve into the nuances of adapting to and working within new codebases, akin to aligning with a shared mental model or vision. They ponder several vital questions that every developer faces when encountering a new project: the balance between exploring a codebase to understand its structure and diving straight into tasks, the decision-making process behind adopting new patterns versus adhering to established ones, and the strategies teams can employ to assist developers who are familiarizing themselves with a new environment. Honeybadger's Exceptional Creatures (https://www.exceptionalcreatures.com/) RailsConf CFP coaching sessions (https://docs.google.com/forms/d/e/1FAIpQLScZxDFaHZg8ncQaOiq5tjX0IXvYmQrTfjzpKaM_Bnj5HHaNdw/viewform) HTTP Cats (https://http.cat/) Support and Maintenance Episode (https://bikeshed.thoughtbot.com/409) Transcript:  JOËL: Hello and welcome to another episode of The Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I'm Joël Quenneville. STEPHANIE: And I'm Stephanie Minn. And together, we're here to share a bit of what we've learned along the way. JOËL: So, Stephanie, what's new in your world? STEPHANIE: I have a delightful and cute Ruby thing to share I'd seen just in our internal company Slack. Honeybadger, the error monitoring service, has created a cute little webpage called exceptionalcreatures.com, where they've basically illustrated and characterized various common Ruby errors into little monsters [laughs], and I find them adorable. I think their goal is also to make it a really helpful resource for people encountering these kinds of errors, learning about them for the first time, and figuring how to triage or debug them. And I just think it's a really cool way of, like, making it super approachable, debugging and, you know, when you first encounter a scary error message, can be really overwhelming, and then Googling about it can also be equally [chuckles] overwhelming. So, I just really liked the whimsy that they kind of injected into something that could be really hard to learn about. Like, there are so many different error messages in Ruby and in Rails and whatever other libraries you're using. And so, that's kind of a...I think they've created a one-stop shop for, you know, figuring out how to move forward with common errors. And I also like that it's a bit of a collective effort. They're calling it, like, a bestiary for all the little creatures [laughs] that they've discovered. And I think you can, like, submit your own favorite Ruby error and any guidance you might have for someone trying to debug it. JOËL: That's adorable. It reminds me a little bit of HTTP status codes as cat memes site. It has that same energy. One thing that I think is really interesting is that because it's Honeybadger, they have stats on, like, frequency of these errors, and a lot of these ones are tied to...I think they're picking some of the most commonly surfaced errors. STEPHANIE: Yeah, there's little, like, ratings, too, for how frequently they occur, kind of just like, I don't know, Pokémon [laughs] [inaudible 02:31]. I think it's really neat that they're using something like a learning from their business or maybe even some, like, proprietary information and sharing it with the world so that we can learn from it. JOËL: I think one thing that's worth specifying as well is that these are specific exception classes that get raised. So, they're not just, like, random error strings that you see in the wild. They don't often have a whole lot of documentation around them, so it's nice to see a dedicated page for each and a little bit of maybe how this is used in the real world versus maybe how they were designed to be used. Maybe there's a line or two in the docs about, you know, core Ruby when a NoMethodError should be raised. How does NoMethodError actually get used, you know, in real life, and the exceptions that Honeybadger is capturing. That's really interesting to see. STEPHANIE: Yeah, I like how each page for the exception class, and I'm glad you made that distinction, is kind of, like, crowdsourced guidance and information from the community, so I think you could even, you know, contribute to it if you wanted. But yeah, just a fun, little website to bring you some delight when you're on your next head-smacking, debugging adventure [laughs]. JOËL: And I love that it brings some joy to the topic, but, honestly, I think it's a pretty good reference. I could see myself linking to this anytime I want to have a deeper discussion on exceptions. So, maybe there's a code review, and maybe I want to suggest that we raise a different error than the one that we're doing. I could see myself in that GitHub comment being like, "Oh, instead of, you know, raising an exception here, why don't we instead raise a NoMethodError or something like that?" And then link to the bestiary page. STEPHANIE: So, Joël, what's new in your world? JOËL: So, just recently, RailsConf announced their call for proposals. It's a fairly short period this year, only about three-ish weeks long. So, I've been really encouraging colleagues to submit and trying to be a resource for people who are interested in speaking at conferences. We did a Q&A session with a fellow thoughtboter, Aji Slater, who's also a former RailsConf speaker, about what makes for a good talk, what is it like to submit to a call for proposals, you know, kind of everything from the process from having an idea all the way to stage presence and delivering. And there's a lot of great questions that got asked and some good discussion that happened there. STEPHANIE: Nice. Yeah, I think I have noticed that you are doing a lot more to help, especially first-time speakers give their first conference talk this year. And I'm wondering if there's anything you've learned or any hopes and dreams you have for kind of the amount of time you're investing into supporting others. JOËL: What I'd like to see is a lot of people submitting proposals; that's always a great thing. And, a proposal, even if it doesn't get accepted, is a thing that you can resubmit. And so, having gone through the effort of building a proposal and especially getting it maybe peer-reviewed by some colleagues to polish your idea, I think is already just a really great exercise, and it's one that you can shop around. It's one that you can maybe convert into a blog post if you need to. You can convert that into some kind of podcast appearance. So, I think it's a great way to take an idea you're excited about and focus it, even if you can't get into RailsConf. STEPHANIE: I really like that metric for success. It reminds me of a writer friend I have who actually was a guest on the show, Nicole Zhu. She submits a lot of short stories to magazines and applications to writing fellowships, and she celebrates every rejection. I think at the end of the year, she, like, celebrates herself for having received, you know, like, 15 rejections or something that year because that meant that she just went for it and, you know, did the hard part of doing the work, putting yourself out there. And that is just as important, you know, if not more than whatever achievement or goal or the idea of having something accepted. JOËL: Yeah, I have to admit; rejection hurts. It's not a fun thing to go through. But I think even if you sort of make it to that final stage of having written a proposal and it gets rejected, you get a lot of value out of that journey sort of regardless of whether you get accepted or not. So, I encourage more people to do that. To any of our listeners who are interested, the RailsConf call for proposals goes through February 13th, 2024. So, if you are listening before then and are inspired, I recommend submitting. If you're unsure of what makes for a good CFP, RailsConf is currently offering coaching sessions to help craft better proposals. They have one on February 5th, one on February 6th, and one on February 7th, so those are also options to look into if this is maybe your first time and you're not sure. There's a signup form. We'll link to it in the show notes. STEPHANIE: So, another update I have that I'm excited to get into for the rest of the episode is my recent work on our support and maintenance team, which I've talked about on the show before. But for any listeners who don't know, it's a kind of sub-team at thoughtbot that is focused on helping maintain multiple client projects at a time. But, at this point, you know, there's not as much active feature development, but the work is focused on keeping the codebase up to date, making any dependency upgrades, fixing any bugs that come up, and general support. So, clients have a team to kind of address those things as they come up. And when I had last talked about it on the podcast, I was really excited because it was a bit of a different way of working. I felt like it was very novel to be, you know, have a lot of different projects and domains to be getting into. And knowing that I was working on this team, like, short-term and, you know, it may not be me in the future continuing what I might have started during my rotation, I thought it was really interesting to be optimizing towards, like, completion of a task. And that had kind of changed my workflow a bit and my process. JOËL: So, now that you've been doing work on the support and maintenance team for a while and you've kind of maybe gotten more comfortable with it, how are you generally feeling about this idea of sort of jumping into new codebases all the time? STEPHANIE: It is both fun and more challenging than I thought it would be. I tend to actually really enjoy that period of joining a new team or a project and exploring, you know, a codebase and getting up to speed, and that's something that we do a lot as consultants. But I think I started to realize that it's a bit of a tricky balance to figure out how much time should I be spending understanding what this codebase is doing? Like, how much of the application do I need to be understanding, and how much poking around should I be doing before just trying to get started on my first task, the first starter ticket that I'm given? There's a bit of a balance there because, on one hand, you could just immediately start on the task and kind of just, you know, have your blinders [chuckles] on and not really care too much about what the rest of the code is doing outside of the change that you're trying to make. But that also means that you don't have that context of why certain things are the way they are. Maybe, like, the way that you want to be building something actually won't work because of some unexpected complexity with the app. So, I think there, you know, needs to be time spent digging around a little bit, but then you could also be digging around for a long time [chuckles] before you feel like, okay, I finally have enough understanding of this new codebase to, like, build a feature exactly how a seasoned developer on the team might. JOËL: I imagine that probably varies a little bit based on the task that you're doing. So, something like, oh, we want to upgrade this codebase to Ruby 3.3, probably requires you to have a very different understanding of the codebase than there's a bug where submitting a comment double posts it, and you have to dig into that. Both of those require you to understand the application on very different levels and kind of understand different mental models of what the app is doing. STEPHANIE: Yeah, absolutely. That's a really good point that it can depend on what you are first asked to work on. And, in fact, I actually think that is a good guidepost for where you should be looking because you could develop a mental model that is just completely unrelated [chuckles] to what you're asked to do. And so, I suppose that is, you know, usually a good place to start, at least is like, okay, I have this first task, and there's some understanding and acceptance that, like, the more you work on this codebase, the more you'll explore and discover other parts of it, and that can be on a need to know kind of basis. JOËL: So, I'm thinking that if you are doing something like a Ruby upgrade or even a Rails upgrade, a lot of what you care about the app is going to be on a more mechanical level. So, you want to know what gems you're using. You want to know what different patterns are being used, maybe how callbacks are happening, any particular features that are version-specific that are being used, things like that. Whereas if you're, you know, say, fixing a bug, you might care a lot more about some of the product-level concerns. What are we actually trying to do here? What is the expected user experience? How does this deviate from that? What were the underlying mental models of the developers? So, there's almost, like, two lenses you can look at the code. Now, I almost want to make this a two-dimensional thing, where you can look at it either from, like, a very kind of mechanical lens or a product lens in one axis. And then, on the other axis, you could look at it from a very high-level 10,000-foot view and maybe zoom in a little bit where you need, versus a very localized view; here's where the bug is happening on this page, and then sort of zoom out as necessary. And I could see different sorts of tasks falling in different quadrants there of, do I need a more mechanical view? Do I need a more product-focused view? And do I need to be looking locally versus globally? STEPHANIE: Wow. I can't believe you just created a Cartesian graph [laughs] for this problem on the fly. But I love it because I do think that actually lines up with different strategies I've taken before. It's like, how much do you even look at the code before deciding that you can't really get a good picture of it, of what the product is, without just poking around from the app itself? I actually think that I tend to start from the code. Like, maybe I'll see a screenshot that someone has shared of the app, you know, like a bug or something that they want me to fix, and then looking for that text in the code first, and then trying to kind of follow that path, whereas it's also, you know, perfectly viable to try to see the app being used in production, or staging, or something first to get a better understanding of some of the business problems it's trying to solve. JOËL: When you jump into a new codebase, do you sort of consciously take the time to plan your approach or sort of think about, like, how much knowledge of this new codebase do I need before I can, like, actually look at the problem at hand? STEPHANIE: Ooh, that's kind of a hard question to answer because I think my experience has told me enough times that it's never what I think it's going to [laughs] be, not never, but it frequently surprises me. It has surprised me enough times that it's kind of hard to know off the bat because it's not...as much as we work in frameworks that have opinions and conventions, a lot of the work that happens is understanding how this particular codebase and team does things and then having to maybe shift or adjust from there. So, I think I don't do a lot of planning. I don't really have an idea about how much time it'll take me because I can't really know until I dive in a little bit. So, that is usually my first instinct, even if someone is wanting to, like, talk to me about an approach or be, like, "Hey, like, how long do you think this might take based on your experience as a consultant?" This is my first task. Oftentimes, I really can't say until I've had a little bit of downtime to, in some ways, like, acquire the knowledge [chuckles] to figure that out or answer that question. JOËL: How much knowledge do you like to get upfront about an app before you dive into actually doing the task at hand? Are there any things, like, when you get access to a new codebase, that you'll always want to look at to get a sense of the project before you look at any tickets? STEPHANIE: I actually start at the model level. Usually, I am curious about what kinds of objects we're working with. In fact, I think that is really helpful for me. They're like building blocks, in order for me to, like, conceptually understand this world that's being represented by a codebase. And I kind of either go outwards or inwards from there. Usually, if there's a model that is, like, calling to me as like, oh, I'll probably need to interact with, then I'll go and seek out, like, where that model is created, maybe through controllers, maybe through background jobs, or something like that, and start to piece together entry points into the application. I find that helpful because a lot of the times, it can be hard to know whether certain pages or routes are even used at all anymore. They could just be dead code and could be a bit misleading. I've certainly been misled [chuckles] more than once. And so, I think if I'm able to pull out the main domain objects that I notice in a ticket or just hear people talk about on the team, that's usually where I gravitate towards first. What about you? Do you have a place you like to start when it comes to exploring a new codebase like that? JOËL: The routes file is always a good sort of overview of, like, what is going on in the app. Scanning the models directory is also a great start in a Rails app to get a sense of what is this app about? What are the core nouns in our vocabulary? Another thing that's good to look for in a codebase is what are the big types of patterns that they tend to use? The Rails ecosystem goes through fads, and, over time, different patterns will be more popular than others. And so, it's often useful to see, oh, is this an app where everything happens in service objects, or is this an app that likes to rely on view components to render their views? Things like that. Once you get a sense of that, you get a little bit of a better sense of how things are architected beyond just the basic MVC. STEPHANIE: I like that you mentioned fads because I think I can definitely tell, you know, how modern an app is or kind of where it might be stuck in time [chuckles] a little bit based on those patterns and libraries that it's heavily utilizing, which I actually find to be an interesting and kind of challenging position to be in because how do you approach making changes to a codebase that is using a lot of patterns or styles from back in the day? Would you continue following those same patterns, or do you feel motivated to introduce something new or kind of what might be trendy now? JOËL: This is the boring answer, but it's almost never worth it to, like, rewrite the codebase just to use a new pattern. Just introducing the new pattern in some of the new things means there are now two patterns. That's also not a great outcome for the team. So, without some other compelling reason, I default to using the established patterns. STEPHANIE: Even if it's something you don't like? JOËL: Yes. I'm not a huge fan of service objects, but I work in plenty of codebases that have them, and so where it makes sense, I will use service objects there. Service objects are not mutually exclusive with other things, and so sometimes it might make sense to say, look, I don't feel like I can justify a service object here. I'll do this logic in a view, or maybe I'll pull this out into some other object that's not a service object and that can live alongside nicely. But I'm not necessarily introducing a new pattern. I'm just deciding that this particular extraction might not necessarily need a service object. STEPHANIE: That's an interesting way to describe it, not as a pattern, but as kind of, like, choosing not to use the existing [chuckles] pattern. But that doesn't mean, like, totally shifting the architecture or even how you're asking other people to understand the codebase. And I think I'm in agreement. I'm actually a bit of a follower, too, [laughs], where I want to, I don't know, just make things match a little bit with what's already been created, follow that style. That becomes pretty important to me when integrating with a team in a codebase. But I actually think that, you know, when you are calibrating to a codebase, you're in a position where you don't have all that baggage and history about how things need to be. And maybe you might be empowered to have a little bit more freedom to question existing patterns or bring some new ideas to the team to, hopefully, like, help the code evolve. I think that's something that I struggle with sometimes is feeling compelled to follow what came before me and also wanting to introduce some new things just to see what the team might think about them. JOËL: A lot of that can vary depending on what is the pattern you want to introduce and sort of what your role is going to be on that team. But that is something that's nice about someone new coming onto a project. They haven't just sort of accepted that things are the way they are, especially for things that the team already doesn't like but doesn't feel like they have the energy to do anything better about it. So, maybe you're in a codebase where there's a ton of Ruby code in your ERB templates, and it's not really a pattern that you're following. It's just a thing that's there. It's been sort of the path of least resistance for a long time, and it's easier to add more lines in there, but nobody likes it. New person joins the team, and their naive exuberance is just like, "We can fix it. We can make it better." And maybe that's, you know, going back and rewriting all of your views. That's probably not the best use of their time. But it could be maybe the first time they have to touch one of these views, cleaning up that one and starting a conversation among the team. "Hey, here are some patterns that we might like to clean up some of these views instead," or "Here are maybe some guidelines for anything new that we write that we want to do to keep our views clean," and sort of start moving the needle in a positive direction. STEPHANIE: I like the idea of moving the needle. Even though I tend to not want to stir the pot with any big changes, one thing that I do find myself doing is in a couple of places in the specs, just trying to refactor a bit away from using lets. There were some kind of forward-thinking decisions made before when RSpec was basically going to deprecate using the describe block without prepending it with their module, so just kind of throwing that in there whenever I would touch a spec and asking other people to do the same. And then, recently, one kind of, like, small syntax thing that I hadn't seen before, and maybe this is just because of the age of the codebases in which I'm working, the argument forwarding syntax in Ruby that has been new, I mean, it's like not totally new anymore [laughs], but throwing that in there a little every now and then to just kind of shift away from this, you know, dated version of the code kind of towards things that other people are seeing and in newer projects. JOËL: I love harnessing that energy of being new on a project and wanting to make things better. How do you avoid just being, you know, that developer, though, that's new, comes in, and just wants to change everything for the sake of change or for your own personal opinions and just kind of moves things around, stirs the pot, but doesn't really contribute anything net positive to the team? Because I've definitely seen that as well, and that's not a good first contribution or, you know, contribution in general as a newer team member. How do we avoid being that person while still capitalizing on that energy of being someone new and wanting to make a positive impact? STEPHANIE: Yeah, that's a great point, and I kind of alluded to this earlier when I asked, like, oh, like, even if you don't like an existing syntax or pattern you'll still follow it? And I think liking something a different way is not a good enough reason [chuckles]. But if you are able to have a good reason, like I mentioned with the RSpec prepending, you know, it didn't need to happen now, but if we would hope to upgrade that gem eventually, then yeah, that was a good reason to make that change as opposed to just purely aesthetic [laughs]. JOËL: That's one where there is pretty much a single right answer to. If you plan to keep staying up to date with versions of RSpec, you will eventually need to do all these code changes because, you know, they're deprecating the old way. Getting ahead of that gradually as we touch spec files, there's kind of no downside to it. STEPHANIE: That's true, though maybe there is a person who exists out there who's like, "I love this old version of RSpec, and I will die on this hill that we have to stay on [laughs] it." But I also think that I have preferences, but I'm not so attached to them. Ideally, you know, what I would love to receive is just, like, curiosity about like, "Oh, like, why did you make this change?" And just kind of share my reasoning. And sometimes in that process, I realize, you know, I don't have a great reason, and I'll just say, "I don't have a great reason. This is just the way I like it. But if it doesn't work for you, like, tell me, and I'll consider changing it back. [chuckles]" JOËL: Maybe that's where there's a lot of benefit is the sort of curiosity on the part of the existing team and sort of openness to both learn about existing practices but also share about different practices from the new teammate. And maybe that's you're coming in, and you have a different style where you like to write tests, maybe without using RSpec's let syntax; the team is using it. Maybe you can have a conversation with the team. It's almost certainly not worth it for you to go and rewrite the entire test suite to not use let and be like, "Hey, first PR. I made your test better." STEPHANIE: Hundreds of files changed, thousands [laughs] of lines of code. I think that's actually a good segue into the question of how can a team support a new hire or a new developer who is still calibrating to a codebase? I think I'm curious about this being different from onboarding because, you know, there are a lot of things that we already kind of expect to give some extra time and leeway for someone who's new coming in. But what might be some ways to support a new developer that are less well known? JOËL: One that I really like is getting them involved as early as possible in code review because then they get to see the patterns that are coming in, and they can be involved in conversations on those. The first PR you're reviewing, and you see a bunch of tests leaning heavily on let, and maybe you ask a question, "Is this a pattern that we're following in this codebase? Did we have a particular motivation for why we chose this?" And, you know, and you don't want to do it in a sort of, like, passive-aggressive way because you're trying to push something else. It has to come from a place of genuine curiosity, but you're allowing the new teammate to both see a lot of the existing patterns kind of in very quick succession because you see a pretty good cross-section of those when you review code. And also, to have conversations about them, to ask anything like, "Oh, that's unusual. I didn't know we were doing that." Or, "Hey, is this a pattern that we're doing kind of just local to this subsystem, or is this something that's happening all the way? Is this a pattern that we're using and liking? Is this a thing that we were doing five years ago that we're phasing out, but there's still a few of them left?" Those are all, I think, great questions to ask when you're getting started. STEPHANIE: That makes a lot of sense. It's different from saying, "This is how we do things here," and expecting them to adapt or, you know, change to fit into that style or culture, and being open to letting it evolve based on the new team, the new people on the team and what they might be bringing to the table. I like to ask the question, "What do you need to know?" Or "What do you need to be successful?" as opposed to telling them what I think they need [laughs]. I think that is something that I actually kind of recently, not regret exactly, but I was kind of helping out some folks who were going to be joining the team and just trying to, like, shove all this information down their throats and be like, "Oh, and watch out for these gotchas. And this app uses a lot of callbacks, and they're really complex." And I think I was maybe coloring their [chuckles] experience a little bit and expecting them to be able to drink from the fire hose, as opposed to trusting that they can see for themselves, you know, like, what is going on, and form opinions about it, and ask questions that will support them in whatever they are looking to do. When we talked earlier about the four different quadrants, like, the kind of information they need to know will differ based off of their task, based off of their experience. So, that's one way that I am thinking about to, like, make space for a new developer to help shape that culture, rather than insisting that things are the way they are. JOËL: It can be a fine balance where you want to be open to change while also you have to remain kind of ruthlessly pragmatic about the fact that change can be expensive. And so, a lot of changes you need to be justified, and you don't want to just be rewriting your patterns for every new employee or, you know, just to follow the latest trends because we've seen a lot of trends come and go in the Rails ecosystem, and getting on all of them is just not worth our time. STEPHANIE: And that's the hard truth of there's always trade-offs [laughs] in software development, isn't that right? JOËL: It sure is. You can't always chase the newest shiny, as fun as that is. STEPHANIE: On that note, shall we wrap up? JOËL: Let's wrap up. STEPHANIE: Show notes for this episode can be found at bikeshed.fm. JOËL: This show has been produced and edited by Mandy Moore. STEPHANIE: If you enjoyed listening, one really easy way to support the show is to leave us a quick rating or even a review in iTunes. It really helps other folks find the show. JOËL: If you have any feedback for this or any of our other episodes, you can reach us @_bikeshed, or you can reach me @joelquen on Twitter. STEPHANIE: Or reach both of us at hosts@bikeshed.fm via email. JOËL: Thanks so much for listening to The Bike Shed, and we'll see you next week. ALL: Byeeeeeeee!!!!!!! AD: Did you know thoughtbot has a referral program? If you introduce us to someone looking for a design or development partner, we will compensate you if they decide to work with us. More info on our website at: tbot.io/referral. Or you can email us at referrals@thoughtbot.com with any questions.

    Claim The Bike Shed

    In order to claim this podcast we'll send an email to with a verification link. Simply click the link and you will be able to edit tags, request a refresh, and other features to take control of your podcast page!

    Claim Cancel