Pro-tips for software engineering interns

A significant portion of my summer was spent mentoring interns at GitHub. In full Silicon Valley flavor—the fleet of interns comprised of bright, high-achieving, ambitious students hand-picked from good schools.

I learned so much from them. In particular, working with them allowed me to hone in on common obstacles that impede the progress of new developers. It became painfully apparent that these challenges are avertible, but no formal guidance is produced on overcoming many “obvious” impediments. Despite lack of instruction, most entry jobs expect a requisite level of workflow know-how to develop organically. This causes the assumed tribal knowledge to be overlooked by most onboarding experiences, leaving new hires and interns to discover idiosyncratic workflows by traipsing into them haphazardly.

I noticed a pattern when many of the interns repeated similar questions about developer workflow, Git, best practices and general problem-solving sensibilities when working on a complex system. In response to the growing list of questions, I wrote up a list of “new engineer pro-tips”—a combination of coding and non-coding skills that could accelerate their output and help them avoid common pitfalls.

Although the original post was internally-facing and far more specific to GitHub, I’ve generalized it for public use in hopes that a more generic set of software engineering guidance may benefit those starting their careers.

Caveat: GitHub is a Rails shop, so much of the following is relevant to Ruby/Rails.

Tips to accelerate your output as a software engineer

1. Testing

The habit of test-driven-development is incredibly useful. A code base as massive as GitHub’s requires you to problem-solve with incomplete information about the system, meaning you’re bound to stumble upon fun surprises, edge-cases and idiosyncrasies. To minimize costly re-writes and quickly validate a solution, it’s typically good to think through test cases and write tests before you write code. Fostering this discipline helps you discover edge-cases in a more methodical way than stumbling through them haphazardly.

Testing tools and techniques

  • Debuggers (such as byebug or pry) will serve you heavily. They open a console when you run a test and allow you to probe different objects, understand their state, look at what your code is returning, and develop a better understanding of expected vs. actual behavior.
  • Run relevant tests only. Depending on your test framework and accompanying gems (ex., guard-rspec, minitest-focus), there are ways to focus on executing a targeted set of tests instead of running every test in a particular file (for example, by dropping the statement “focus” above a particular test or set of tests). This enables you to concentrate your testing on what you care about and resultantly save time by only running relevant tests.
  • Triangulate from a variety of outputs: use a combination of the console, your server output depending on the problem you’re solving.
  • Server-side vs. client-side testing: learn when to use each, what types of tests to conduct in the browser vs. elsewhere (ex., [this Chrome dev tools overview](https://developer.chrome.com/devtools) is aite).
    - Security: keep a practice of writing tests that ensure you’re protecting against malicious attacks on your server resources and databases.

General rules for when to write tests and when not to

Test first:

  • If test is short/simple compared to application code.
  • If test is for security model (because security is top priority).
  • Whenever a bug is found, write a test to reproduce it and protect it against regressions, then write the application code to fix it.
  • Write tests before refactoring code, focusing on testing error-prone code that’s especially likely to break.
  • Generally write controller and model tests first, and integration tests (testing functionality across models, views and controllers) second. Integration tests are cool because they allow us to simulate user behavior and test composite functionality.

Application code first:

  • When the desired behavior isn’t clear, write application code first and then a test to codify behavior.
  • Don’t write tests for code (such as detailed HTML structure) likely to change in the future.
  • Skip testing when code isn’t brittle or is likely to change, skip testing.
  • When you are testing server side code: you are testing the logic and readiness of your application.
  • When you are testing web side code: you are doing end to end testing.

Rails testing

GitHub uses MiniTest. The anatomy of a test will typically look like this:

test “describe what you’re testing” do
  # 1. Setup conditions for your test (ex., stubbing data).
 # 2. Perform the function you want to test.
 # 3. Assertions: see if the function performed as expected.
end

Refer to the Rails testing guide for more thorough documentation on MiniTest and assertions. If you are courting the idea of building a Rails app yourself, be sure to check out RSpec, another popular testing framework.

2. Keyboard shortcuts and linux commands

Development necessitates navigating between your editor, terminal and multiple browser tabs. Keyboard shortcuts that allow you to quickly manipulate your digital space. This makes context-switching easier, which is necessary when you’re racing against your brain’s faulty memory to maintain the maximum amount of information from multiple sources (ex., StackOverflow, the server output, etc.) while doing a complex query and also remembering the problem you’re solving.

Learn Enough Command Line to Be Dangerous is a good resource to lean on.

3. Git and workflow

Many of the following questions were repeated a lot:

  • “How many commits should my branch have?”
  • “How big should a commit be?”
  • “What’s a good commit message?”

There are accepted practices around this stuff. Rather than replicating existing wisdom here, I’d refer you to an interactive CodeSchool course on Git.

4. Make your workspace pretty

Another thing that sounds stupid but helps.

De-clutter your screen
Having an organized, decluttered digital workspace actually goes a long way. SizeUp has keyboard shortcuts that can help you keep your screen clean and less cluttered.

Soup up yo bash profile
You can customize your prompt with colors and information that’s most convenient for you may help. @mdo has a pretty lit one that might be worth borrowing from as a baseline and adapting to your own needs.

Soup up yo editor
Depending on what you’re using, you can optimize it to do more heavy-lifting for you. Here are some cool Atom packages that may be of interest.

Systems thinking and decision making
There are often 10 different ways to do something. Software development principles and practices are often subjective and dependent on the problem being solved, its constraints and the ideological beliefs of the team, organization or individuals you’re working with. This process is never perfectly empirical, but there are philosophies to lean on and use as a crutch when making decisions around where to put your code, how to test it, etc.

6. Confidently asking questions

Part of being a good engineer is clarifying requirements and expectations upfront. However, asking questions can be uncomfortably vulnerable, and presupposes a level of inner-conviction that is difficult to have early in your career.

When asking questions or seeking help, it’s generally useful to share what problem you’re solving, why you’re solving it, and stating information you already know. This allows the other person to quickly build context and also shows what you’ve investigated already, narrowing the scope for a more specific conversation.

  • How to say it at work: a book I’ve been recommended on how to frame things appropriate for a professional context. Caveat: I haven’t read yet, so I can’t vouch for how effective it is.
  • Choosing a medium: learning to distinguish between the types of communication that’s best over Slack/in-person/in a pull request, issue, email, video-conferencing, etc.
  • Write to strengthen intuition: I still struggle heavily with being vocal about my ideas. One thing I’ve been doing to build confidence is keeping a list of things I had good intuition about but didn’t say out loud (because I was unsure). When the thing I was unsure about is later validated, I document it to learn from the evidence I was lacking in feeling more confident to share it publicly. It allows me to pick up on patterns and better build connections that close the gap between intuition and tangible technical knowledge.

7. Comfort with inactivity

This can feel counter-intuitive, but taking a step back to scope out a problem, do research or ramp up on a new area is sometimes more important than jumping into code. This is too often falsely conflated with laziness. There’s a natural inclination to feel productive which can lead you into a trap of putting thoughts to keyboard without having a robust blueprint or plan in place. This costs you later if you end up designing sub-optimal solutions or have to backtrack.

There’s of course a balance here to keep your productivity in check; using the following guideposts is typically helpful:

  • Make sure your manager/team-mates know what you’re doing and why you’re spending extra time on something.
  • Make sure no one is blocked.
  • Get feedback early if you’re digging into something (ex., chasing creative fancies that lead you to implement entirely-new-and-inconsistent-but-super-interesting design patterns is probably a time-wasting distraction your manager can guide you away from).
  • If you’re stuck on a problem for 20+ minutes, ask someone for help.

8. Learning together

Internships benefit from a type of camaraderie that’s difficult to replicate once you enter the workforce full-time. Use this unique closeness as a support system to start reading lists, book clubs or collaborate on side-projects. Hopefully this doesn’t sound prescriptive; it speaks from my personal experience of doing 6 tech internships where I regret being way too shy and not relying more on the awesome cohorts I was part of. 🙈

Ending with a note of gratitude

I can not overstate how lucky I feel to have learned from GitHub’s 2017 interns. Beyond their commitment to building technology—it was refreshing to see the “next generation” of software engineers genuinely devoted to critically engaging in the social aspects of our industry. Thank you for your passion toward evolving standards for equality and diversity, in parallel with your technical and professional aspirations.