<?xml version='1.0' encoding='utf-8' ?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Social Memory Complex: railsconf</title>
<link href="https://www.socialmemorycomplex.net/tags/railsconf/feed.xml" rel="self" />
<link href="https://www.socialmemorycomplex.net/tags/railsconf/" />
<updated>2026-05-24T21:17:06+00:00</updated>
<id>https://www.socialmemorycomplex.net/tags/railsconf/</id>
<entry>
  <title>RailsConf Dispatch - Test Always?</title>
  <link href="http://socialmemorycomplex.net/2010/06/08/railsconf-dispatch-test-always/" />
  <updated>2010-06-08T00:00:00+00:00</updated>
  <id>http://socialmemorycomplex.net/2010/06/08/railsconf-dispatch-test-always/</id>
  <author><name>Jeremy Weiland</name></author>
  <content type="html"><![CDATA[<p>There were two conveniently sequential presentations today at RailsConf that reminded me of some thoughts I’d had regarding testing: <a href="https://en.oreilly.com/rails2010/public/schedule/detail/14131">Michael Feathers</a>’ talk on legacy code and <a href="https://en.oreilly.com/rails2010/public/schedule/detail/14216">Glenn Vanderberg</a>’s talk on real software engineering. It seems to me that both talks had a theme in common: what is the function of tests? Why do we want them, what role do they play from an engineering perspective in the larger process, and what precisely are they meant to indicate to us?</p>

<p>Michael at one point talked about the expense of 100% code coverage for tests, instead recommending we test the parts of the code that change the least and are most important. Ugly code in legacy projects has utility, he explained, and untested code is a rational response to churn. Afterwards, Glenn discussed software development in the context of engineering principles from older, more established disciplines like structural engineering, finding areas of similarity, analogy, and abject difference. However, his testing point compared experiments in code to experiments in more physical engineering fields, remarking on how relatively cheap tests are for us. I suppose the common thread I found concerned the emphasis on cost: that what it <em>means</em> for us to do our job well is to do it effectively, and not subordinate our conscience and creativity to a mechanical process.</p>

<p>For some background, I’ve been practicing behavior driven development for a year or two. I love the confidence that testing gives me, independent of the value to the client. Verifying that my code works is fine and all, but what lets me sleep at night is the assurance derived from approaching a problem in a rational, systemic manner. By moving in small chunks and expressing problems in terms I understand well enough to programmatically recreate, I ground myself in a real comprehension of the system I’m building at the most relevant level and stage. I avoid the confusion of jumping ahead, thinking too large scale or minutely, or making unwarranted assumptions that come back to bite.</p>

<p>But I’ve found there are definitely times when testing first is the wrong approach. Remember: testing is supposed to reinforce your understanding of the problem. But what happens when you fundamentally don’t understand it? When you first encounter the project, you don’t necessarily <em>have</em> expectations or any way of identifying what a successful outcome is. Test first is supposed to get you to think about these things, but there’s no substitute for writing and running code.</p>

<p>Experimentation, trial and error, and playing around are important discovery mechanisms that give us the understanding we then apply to more rigorous processes. Spending time writing tests that do nothing but reinforce the fact that you don’t know what you’re doing is stupid. I’ve found that you have to think carefully about what you’re expecting your tests to actually accomplish for you, as you can dig yourself down rabbit holes needlessly by stressing form over function.</p>

<p>Similarly, as Feathers pointed out, even well understood requirements and algorithms often cost more to test than its worth. Code that changes often can cause test churn that burns up effort needlessly. And often we write large swaths of code that, while useful, just isn’t that critical to the success or failure of the project. Given these tensions, 100% coverage may not simply be an unreasonable goal - it may be positively wasteful.</p>

<p>Again, the problem is deeper, as Glenn pointed out in his talk. Historically (and surprisingly in spite of salient warnings as early as the 60’s) “Software Engineering” with capital letters has been biased towards a philosophy of formal, defined process models that demand predictability and reproducibility. Although behavior driven development may seem to encapsulate this philosophy, remember that when we’re dealing with behavior we’re not necessarily realizing mathematical precision at the level many engineers would expect. Accuracy is important in some engineering contexts, but flexibility can sometimes trump it.</p>

<p>A final thought that I consider my unique contribution to this discussion: if you do test first, consider that your tests may be disposable. It’s great that you are using tests to drive the development, and building a test suite imparts tremendous satisfaction as you pile on more and more features. But just because a given test is useful to <em>writing the code</em> doesn’t mean it’s useful for <em>verifying the project’s success</em>. It may in fact be, but that should be a demonstrable standard - don’t let your personal satisfaction detract you from the big picture goal of the project.</p>

<p>100% code coverage is increasingly seen as superfluous because areas of code churn are just a fact of software development. You may find the changes are so targeted and frequent that more exploration and manual play pays off more than working out programmatic unit tests. Also, why wouldn’t your test suite have cruft just like your source code? Perhaps your legacy tests reflect an emphasis on certain requirements that are just too rigid. In that sense, paring down the tests-as-specs is entirely consistent with the project’s interests as it reflects the actual state of the project. Tests out of sync with current requirements are often worse than no tests at all.</p>

<p>Conventions and best practices are no excuse to forget our responsibilities to the client’s budget. The point is not to adopt a rule that tests are no longer important to maintain; rather, avoid blindly following <em>any</em> rule for its own sake, especially when you’re being paid to use your brain. Your job is not to write code; it’s to deliver client value, and that requires careful thought. Depending on the importance of a given section of code, rewriting that unit test may be less useful than scrapping it for the moment - or perhaps focusing on integration tests instead. Always be willing to step outside your comfort zone to better understand the project and realize it’s criteria for success - even if you lose a bit of sleep over it.</p>
]]></content>
</entry><entry>
  <title>RailsConf Dispatch - Rescue Missions</title>
  <link href="http://socialmemorycomplex.net/2010/06/07/railsconf-dispatch-rescue-missions/" />
  <updated>2010-06-07T00:00:00+00:00</updated>
  <id>http://socialmemorycomplex.net/2010/06/07/railsconf-dispatch-rescue-missions/</id>
  <author><name>Jeremy Weiland</name></author>
  <content type="html"><![CDATA[<p>The first tutorial class at <a href="https://en.oreilly.com/rails2010">RailsConf</a> on <a href="https://s3.amazonaws.com/tammer_saleh/production/assets/vtm_rails_antipatterns.pdf">Rails Anti-Patterns</a> has been phenomenal and incredibly validating given my experiences with consulting. <a href="https://tammersaleh.com/">Tammer Saleh</a> gave a wonderful talk on how to handle troubled legacy codebases - what he calls “rescue missions”. It’s particularly relevant for me as much of my early freelance work centered on failing projects I was dumped into.</p>

<p>Because of the success of Rails, there’s a lot of shitty code out there for you to fix. The harder issue is figuring out why shitty code was delivered, which can be trickier to figure out than you’d think. It can be really difficult to change the course of a project when much more than merely the code is dysfunctional.</p>

<p>Tammer suggested a ton of coping strategies, many of which end up being good practices for most situations. I’m sharing my cursory notes here in case others are interested. Feel free to strike up a conversation in the comments to explore these points. I’ll link to the slides when they become available.</p>

<ul>
  <li>Considering a code rescue mission
    <ul>
      <li>client relationships can be adversarial</li>
      <li>many problems are with process and not just programming, where the programmer didn’t push back on features</li>
      <li>when devs don’t want to follow convention, disaster often ensues</li>
      <li><em>15 min code review</em> to get clear on situation before contract signed
        <ul>
          <li>models
            <ul>
              <li>how many models? Too many is bad.</li>
              <li>search for “assert_true” to see where scaffolding was used</li>
              <li>empty models</li>
              <li>superfluous raw SQL</li>
            </ul>
          </li>
          <li>controllers
            <ul>
              <li>non-RESTful controllers</li>
              <li>pseudo-validations in create actions (manual checks in actions not using conventions but inline)</li>
              <li>monolithic controllers (three controllers &amp; a zillion actions)</li>
              <li>custom authentication</li>
            </ul>
          </li>
          <li>views
            <ul>
              <li>PHP-style inline ruby / SQL in the views</li>
              <li>inconsistent file structure</li>
              <li>poor markup, no rails helpers involved</li>
              <li>layout code in views</li>
              <li>duplication - too DRY can be bad, but duplication should be avoided</li>
            </ul>
          </li>
          <li>lib
            <ul>
              <li>reinventing the wheel</li>
              <li>duck punching - reopening class</li>
            </ul>
          </li>
          <li>general danger signs
            <ul>
              <li>huge files</li>
              <li>feature bloat</li>
              <li>bad ruby style</li>
              <li>gratuitous metaprogramming</li>
            </ul>
          </li>
        </ul>
      </li>
      <li>Once you’ve concluded this is a rescue mission, identify its root causes - it’s never about the code itself
        <ul>
          <li>hard technical problems</li>
          <li>poor developers - hard to identify, you can’t judge from the code necessarily</li>
          <li>process reasons</li>
          <li>personality reasons
            <ul>
              <li>a lack of focus on quality</li>
              <li>artificial deadlines</li>
            </ul>
          </li>
        </ul>
      </li>
      <li>think carefully about taking the job
        <ul>
          <li>reputation damage likely</li>
          <li>client probably doesn’t have much money left</li>
          <li>Core question: <em>can you fix this?</em> Not just the code but client issues that caused bad code.</li>
          <li>Will fixing this result in future good work?</li>
          <li>How much money? This is painful, difficult, and risky work - you should be well paid. Rule of thumb: 50% over base.</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>Proceeding with the rescue mission
    <ul>
      <li>train the client
        <ul>
          <li>all of the work you do on the code is useless if the client keeps their bad habits</li>
          <li>send them the 37 signals book, dog-ear pages to pay attention to</li>
          <li>my job is not to be just a code monkey. I’m here for <em>advice and negotiation on features</em>. Push back!</li>
          <li>force payment of tech debt
            <ul>
              <li>explain long term costs</li>
              <li>70-80% of core development budget</li>
            </ul>
          </li>
          <li>you must be willing to lose the client</li>
        </ul>
      </li>
      <li>fix process
        <ul>
          <li>establish trust</li>
          <li>record everything
            <ul>
              <li>transcript, voice recording, etc.</li>
              <li>documentation for yourself</li>
              <li>communication</li>
              <li><em>visibility</em></li>
            </ul>
          </li>
          <li>weekly standups</li>
          <li>tools
            <ul>
              <li>github - get client subscribed to commit feed for visibility (they don’t need to understand)</li>
              <li>pivotal tracker
                <ul>
                  <li>client prioritization of stories</li>
                  <li>emergent velocity is crucial</li>
                  <li><em>use low velocity as an argument for fixing tech debt</em></li>
                </ul>
              </li>
              <li>basecamp &amp; campfire for communication</li>
            </ul>
          </li>
        </ul>
      </li>
      <li>fix codebase
        <ul>
          <li>peer programming
            <ul>
              <li>transfer domain knowledge</li>
              <li>teaches best practices</li>
              <li>keeps you focused</li>
            </ul>
          </li>
          <li>integration tests are a necessity on rescue missions
            <ul>
              <li>cover common paths</li>
              <li><em>integration tests for existing behavior, functional/unit tests for added or modified behavior</em></li>
            </ul>
          </li>
          <li>work in small chunks, mixing in:
            <ul>
              <li>client value</li>
              <li>low impact, high yield pieces</li>
              <li>reduce tech debt</li>
            </ul>
          </li>
          <li>focus on issues <em>slowing you down</em></li>
          <li>DON’T GET DISTRACTED by all the issues, just record them and move on
            <ul>
              <li>don’t do code comments, because client can’t see that</li>
            </ul>
          </li>
          <li><em>remote tracking</em> feature branches
            <ul>
              <li>isolate refactoring</li>
              <li>increase visibility</li>
              <li>protects you from rabbit hole excursions - you can always trash the whole thing</li>
              <li>git remote branch gem available</li>
            </ul>
          </li>
          <li>balance of refactorings w/ features</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>Other field tactics
    <ul>
      <li>Too many models
        <ul>
          <li>consider a much more denormalized model than often desired</li>
          <li>it’s never just one more model to throw in</li>
          <li><em>extra code is a liability</em></li>
        </ul>
      </li>
    </ul>
  </li>
</ul>
]]></content>
</entry>
</feed>
