Software Engineering Lessons from NASA: Part One

Long before I became a programmer, I worked as an optical engineer at NASA’s Goddard Space Flight Center. I was part of the Optical Alignment and Test group, which is responsible for the integration and testing of science instruments for spacecraft like the Hubble Space Telescope, Swift, and the upcoming James Webb Space Telescope.

While most of the day-to-day engineering practices I learned didn’t transfer over to software engineering, I’ve found certain principles still hold true. Here’s the first one:

The first version you build will be wrong.

At NASA, it was taken as axiomatic that the first time you built an instrument, you’d screw it up. Engineers always pressed for the budget to build an Engineering Test Unit (ETU), which was a fully-working mock-up of the instrument you ultimately wanted to launch. Since the ETU was thrown away afterward, you could use it to practice the techniques you’d use to assemble the real instrument.

And you needed the practice. The requirements were often so tight (to the thousandth of an inch) that no matter how well you’d planned it out, something always went wrong: a screw would prove too hard to reach, or a baffle would be too thin to block all the light.

No amount of planning and peer review could find all the problems. The only way to know for sure if it would work was to build it. By building an ETU, you could shake out all the potential flaws in the design, so the final instrument would be solid.

In software, I’ve found the same principle holds: the first time I solve I problem, it’s never the optimal solution. Oddly enough, I often can’t see the optimal solution until after I’ve solved the problem in some other way.

Knowing this, I treat the first solution as a learning experience, to be returned to down the line and further optimized. I still shoot for the best first solution I can get, but I don’t beat myself up when I look back at the finished product and see the flaws in my design.

Five Reasons Your Company Should Allow Remote Developers

If you don’t allow your software developers to work from home, you’re not just withholding a nice perk from your employees, you’re hurting your business.

Here’s five reasons letting developers work remotely should be the default:

1. Widen your talent pool

Good software engineers are already hard to find. On top of that, you’ve got to hire someone skilled in your chosen tech stack, which narrows the pool even further. Why restrict yourself to those engineers within driving distance to your office? Can you really afford to wait for talent to move close to you?

The CTO for my current gig lives an hour away from the office, and can’t move because of his wife’s job. We wouldn’t have been able to hire him if we didn’t let him work from home. Who are you missing out on because you won’t look at remote devs.

2. Reclaim commute time

The average commute time in the US is 30 minutes. That’s an hour a day your in-office developers aren’t working. If you let them work from home, they can start earlier and finish later.

Don’t think they will? The evidence shows they do: people working from home work longer hours than people in the office.

An extra hour of work a day is five more hours a week, or almost three more work days in the month. Why throw that time away?

3. Reduce sick leave

When I’ve got a cold or flu, I stay home. I usually spend the first day or two resting, but by the third day I’m usually able to work for a little, even though I’m too sick to go into the office.

If my boss didn’t let me work from home, he wouldn’t get those “sick hours” from me. I’d be forced into taking more time off, getting less done.

And when my wife’s sick, I don’t have to choose between taking care of her and getting my work done. By working from home, I can do both.

4. Increase employee retention

The only thing worse than not hiring a good dev is losing an engineer you’ve already got. When they leave, you’re not only losing a resource, you’re losing all the historical knowledge they have about the system: weird bugs that only show up once a quarter, why the team chose X db instead of another, etc.

Since the competition for talent is fierce, you don’t want to lose out to another company. Letting developers work from home sends a clear signal to your employees that they’re valued and that you appreciate work-life balance. And with many companies sticking to the old “gotta be in the office” way of thinking, you’re ensuring that any offer your employees get from another company won’t be as attractive.

5. Stay focused on work done

Finally, letting developers work from home forces the entire team to focus on what’s really important: getting work done. It doesn’t matter how many hours a developer spends in the office; if that time isn’t productive, it’s wasted.

What does matter is how much work a dev gets done: how many bugs squashed, how many new features completed, how many times they jump in to help another engineer. What you need from your developers is software for your business, not time spent in a cubicle. If they’re not producing, they should be let go. If they *are* producing, does it matter if they’re at a coffee shop downtown?

And if you don’t have a way of measuring developer productivity beyond hours spent at their desk, get one. You can’t improve something you don’t measure, and team productivity should be high on your list of things to always be improving.

ClojureWest 2013: Day Three Notes

Editing Clojure with Emacs: Ryan Neufeld

  • emacs live, emacs prelude, emacs-starting-kit: for getting up and running
  • bit.ly/vim-revisited – post about going back to basic editors
  • bit.ly/clj-ready: ryan’s version of emacs starter kit
  • nREPL & nREPL.el instead of inferior-lisp
  • melpa for your package manager
  • have a look at the projectile minor mode for project management
  • use paredit to build up clojure code trees, not just characters
  • slurp and slice to add new structure: () then slurp to wrap

Ritz: Missing Clojure Tooling: Hugo Duncan

  • clojure debugging tool
  • nREPL: lets you connect to remote clojure VM (transport + socket + protocol + REPL)
  • sits in-between your local nrepl and the remote nrepl process
  • available as marmalade package
  • also lein plugin: lein-ritz
  • turn on break on exceptions: gives you common-lisp like stacktrace + restart options (continue, ignore, etc)
  • can open up stacktrace to see local variables within the frame
  • can even evaluate expressions within the frame (!)
  • also pull up the source code for a frame (clojure or java)
  • can set breakpoints on lines of code in emacs; view list of all current breakpoints in a separate buffer
  • warning: lists appear in stacktrace frame fully-realized (will be fixed)

Macros vs Monads: Chris Houser and Jonathan Claggett

  • synthread: library of macros that use Clojure’s -> (threading macro) as the do
  • usually pulled in with -> as its alias
  • defines macros for most of clojure’s control-flow code
  • also has updater macros
  • use for cases where pure functional code is too verbose and/or hard to read
  • replaces use of state monad
  • monads are infectious (end up passing them around everywhere)
  • clojure 1.5 has cond-> macro
  • rover example project up on github: lonocloud/synthread

How to Sneak Clojure into Your Rails Shop: Joshua Bellanco

  • and improve your rails hosting along the way
  • chief scientist at burnside digital (based in portland)
  • if this was 2006, we’d be talking about sneaking rails into your java shop
  • learn from rails: better to ask forgiveness than permission, use clojure whenever it’s the right tool for the job, don’t be afraid to show off your successes
  • step 1: convince them to use jruby (better tooling, better performance, java ecosystem, plenty of hosting options)
  • step 2: torquebox for deployment (benefits of jboss)
  • step 3: go in with clojure and start using immutant overlay (gives jboss, torquebox, jruby); lein immutant run runs both ruby and clojure apps
  • step 4: openshift (open source heroku from redhat)
  • step 5: make clojure and ruby talk to each other (hornetq + torquebox/immutant messaging libraries)
  • step 6: show off!
  • example project: memolisa: sends alerts and confirmation when alert is read; rails for users + groups, clojure for the messaging

clj-mook: Craig Brozefsky

  • library for testing web apps
  • persistent session, default request params, html parsing
  • motivated by shift from ring to immutant

RxJava: Dave Ray

  • open-source implementation of M$ Reactive Extensions
  • operators for creating, composing, and manipulating Observable streams
  • sketch up at daveray/rx-clj
  • also: netflix/rxjava

VMFest: Toni Batchelli

  • goal: make virtualbox behave like lightweight cloud provider
  • instantiate multiple vms from the same image
  • wrap the OOP api of virtualbox with a sane clojure one
  • has hardware dsl
  • one step further: little-fluffy-cloud -> RESTful interface for vmfest
  • visaje: build VMs from data

shafty: Chris Meiklejohn

  • basho technologies: erlang and js, with some clojure
  • functional reactive programming library
  • based on flapjax, implemented in clojurescript
  • implementation of core API from flapjax
  • FRP: authoring reactive programs without callbacks
  • FRP: event streams and behaviors

swearjure: gary fredericks

  • subset of clojure with no alphanumeric characters
  • exercise in extreme constraints
  • integers but no floats
  • true and false available

Evolutionary Art with Clojure: Alan Shaw

  • clevolution: partial implementation of paper on genetic programming
  • converting s-expressions to images
  • using self as filtering function: delete the images he doesn’t like
  • want to move to true evolutionary: use function to weed through the images, allow cross-breeding between s-expressions
  • clisk, clojinc, clevolution

Simulation Testing with Simulant: Stuart Halloway

  • example-based tests: setup, inputs, outputs, validation
  • originally built as tool for testing datomic
  • no language indirection: it’s about using clojure and datomic
  • write model for what will happen to the application, then writeup actions to take against the system
  • can speed up time in simulation, so don’t have to wait for 6 mo for 6 months of results to go through
  • stores db with test results *and* the db tests ran against
  • can find and diagnose bugs by querying db after tests run
  • use clojure.data.generators to generate random test data (ints, vectors, maps)
  • github.com/stuarthalloway/presentations/wiki
  • contact at @stuarthalloway to have come out to speak at company or group
  • even working through the model of users and use is a good exercise; good way to validate the assumptions you’re making about the system

ClojureWest 2013: Day Two Notes

Winning the War on Javascript: Bodil Stokke

  • catnip: beginners clojure editor in browser
  • originally used coffeescript because cljs was immature and hard to work with js objects
  • currently working on converting catnip to cljs
  • error: another clojurescript testing framework, built around asynchronous testing

PuppetDB: Sneaking Clojure into SysAdmins’ Toolkits: Deepak Giridharaghopal

  • ops has a lot of entropy: spooky action at  distance: devs or admins logging in to one of many servers and mucking around without telling you
  • lack of predictability ruins automation and abstraction
  • problems with previous software in Ruby: not fast, only one core, mutable state everywhere, runtime compatibility a problem
  • solution: puppetdb in clojure for storing and querying data about systems
  • used CQRS: command query responsibility separation -> use different model to update then to read info
  • send commands to a /command endpoint, which queues the command for parsing and processing
  • build command processing functions as multi-methods switching off of the command and version sent
  • can also turn on live repl, to connect to running code and hack
  • queries have their own AST-based syntax; sent as json, built as vector tree
  • can ship the whole thing as a single uberjar, with built-in db, etc

Securing Clojure Web Services & Apps with Friend: Chas Emerick

  • authentication & authorization (who are you? what are you allowed to do?)
  • options: spring-security (java, not recommended), sandbar, ring-basic-authentication, clj-oauth2
  • most common: roll your own
  • wrote friend to have a common auth framework
  • uses ad-hoc hierarchies for roles
  • add workflows to specify how to authenticate a request that doesn’t have authentication yet
  • friend-demo.herokuapp.com for multiple demos with source code
  • recommend using b-crypt over sha

FRP in ClojureScript with Javelin: Alan Dipert

  • event stream: sequence of values over time
  • behavior: function that updates according to changing values from event stream
  • reactive evaluation: holds off on evaluating until all values are available
  • similar to spreadsheet formula evaluation (!)
  • FRP maintains evaluation order despite lack of all values at once
  • current FRP in clojurescript: FlapJax
  • javelin: abstract spreadsheet library for reactive programming with values
  • everything contained in the “cell” macro
  • web app has single state at any point in time, contained in the “stem cell”
  • everything in app either in stem cell or derived from it

SQL and core.logic Killed my ORM: Craig Brozefsky

  • uses clojure for analysis engine looking for possible malware actions
  • core.logic engine takes observations and creates IOCs (indications of compromise) + html
  • observations: wrapper around core.logic’s defrel
  • IOCs: severity + confidence, explanation, suggested remediation
  • the reasoned schemer: handed out to all their analysts to explain logic programming to them so they can use the system

Macros: Why, When and How: Gary Fredericks

  • macro: special function that takes form as argument and returns a form
  • run at compile time
  • can always be replaced by its expansion
  • when writing macros, helps to know what you want it to expand to
  • use macroexpand-1 to find out when it’s going to return
  • cannot pass macro to higher-order function (not composable at runtime)
  • macros can make code harder to read; person reading code has to be familiar with macro expansion to really know what your code is doing
  • tolerated usage: defining things, wrapping code execution, delaying execution, capturing code, DSLs, compile-time optimizations (hiccup produces as much html as possible at compile time)
  • avoiding macros: get more familiar with higher-order function usage and paradigms
  • writing tolerable macros: use helper functions, naming conventions, no side effects
  • syntax-quote (backtick): like quote on steroids, gives you multiple benefits when used in a macro

ClojureWest 2013: Day One Notes

Domain Driven Design in Clojure: Amit Rathore

  • read the book from eric evans
  • Lot of oop design principles carry over
  • shoot for 3-4 lines of clojure code per function
  • validateur, bouncer, clj-schema (validation)
  • if code confusing, demand simplification
  • make temp namespaces explicit: zolo.homeless
  • domain: business-important logic, not the API, not services, not validation, not talking to the db, just the stuff business people care about; should be pure
  • if you don’t need it now, don’t build it

RESTful Clojure: Siva Jagadeesan

  • liberator, bishop: libraries to help build proper REST APIs in clojure
  • use the status codes: 1xx – Metadata, 2xx – success, 3xx – redirect, 4xx – client error, 5xx – server error
  • 405: method not allowed
  • 409: conflict
  • 404: resource not present
  • create returns Location header with location of new resource, in addition to the 201 (created) status code
  • even better: also return a set of links to related resource (rel = self) and transitions (rel = cancel)
  • allows client to be loosely coupled from API
  • client doesn’t need to know how resources move through the system (transition logic)
  • REST means using multiple URIs, HTTP status codes, and Hypermedia

Clojure in the Large: Stuart Sierra

  • def’ing refs and atoms at the top level is basically global mutable state via singletons, please avoid
  • recommend using constructor functions to *return* the state variables you want to use, then pass that state along to each function
  • easier to test
  • explicit dependencies
  • safe to reload when working at the repl
  • thread-bound state also bad: assumes no lazy sequence returned in function bodies, hides dependencies, and limits caller to using one resource at a time
  • prefer passing context around to functions
  • can pull resources out of it
  • use namespace-qualified keys for isolation
  • isn’t confined to a single thread
  • still need to cleanup at the end
  • more bookkeeping
  • true constants are fine as global vars (^:const)

Pedestal: Architecture and Services: Tim Ewald

  • alpha release from relevance of open-source libs
  • use clojure end-to-end to build RIAs
  • demo: hammock cafe: clojurescript apps communicating to same back-end using datomic store
  • 2 halves: pedestal-service, pedestal-app
  • ring limits: bound to a single thread’s stack
  • interceptors: map of functions, has enter and leave for processing requests and responses
  • can pause and resume along any thread
  • pushed to be as ring-compatible as possible
  • use of long polling and server-side events (requests that come in slow and last a long time, get updated as more data comes in)

Design, Composition, and Performance: Rich Hickey

  • take things apart
  • design like bartok (embrace constraints, use harmonic sense)
  • code like coltrane (constant practice, keep harmonic sense)
  • build libraries like instruments (design for players, able to be combined with other things)
  • pursue harmony

Why the Perfectible Job Beats the Perfect Job

I’ve changed jobs a lot over the past few years. I knew it would raise eyebrows each time I applied for a new position – they’d ask themselves how long I’d stay with them – but I told myself it was because I was looking for the perfect job, so it was ok.

I’ve realized the perfect job just isn’t out there.

There’s something wrong with every job. The dress code is too formal. The boss won’t let me work from home. They’re not using my favorite language. They’re not promoting me. They don’t give enough vacation hours. The codebase is old and crufty. Etc.

The trick is to find a perfectible job, not a perfect one. A job with a boss and a team that’s flexible enough so that you can improve the job over time.

After all, my interests will shift over time. Even if I found the perfect job for what I want to do now, I’ll grow out of it in a few years. And then where would I be? Leaving the perfect job, and torturing myself over it.

Better to find a job with a few rough edges that I can smooth out over time. As long as I go in with my eyes open, I won’t be disappointed.

Because perfecting your job is just a different type of engineering.

How I Failed to Get Clojure Adopted at my Workplace

I’m a little obsessive about the tools I and my co-workers use. If I come into a company and they’re still on svn, I push to get us switched to git. If people have to login to a remote server to develop on via a slow internet connection, I figure out how to get a local VM install to do everything we need, and share it with everyone.

So when my current company started experiencing pains with PHP, and the other devs talked about switching languages, I immediately thought of persuading them to switch to Clojure.

Why Clojure? It’s fast. It’s code is compact but still readable. And it’s got the power of Lisp.

I got permission to build some internal tools in Clojure, and set to work. I thought it was just a matter of time before the other devs would “see the light” and start switching over to Clojure.

Nine months later, we’re still building everything in PHP. No one’s even looked at my Clojure code. Beyond some jokes about Python, there’s been no further movement toward switching languages.

I’ve failed.

But why? In order to document my mistakes, so I and others can learn from them, here’s what I did wrong:

No Expertise

I’d only been using Clojure for a little over a year when I started building those internal tools. I’d never deployed a Clojure application, didn’t know too much about how to write tests in Clojure, and had only done hobby projects using it.

As a result, those internal tools I built were full of bugs when they launched. When I got pulled off of working on those tools and back to working on the core company applications, those bugs stayed – for months – without being fixed.

My co-workers’ first experience with Clojure was of a buggy, slow application that didn’t do too much. That first impression was hard to shake, even after I went back and fixed bugs and improved the apps.

No Designer

Not only were the web apps slow and buggy when they first launched, they looked terrible. I got so focused on using Clojure that I ignored the user interface, and didn’t get a front-end developer to help me with it.

No Maintenance

Some of the my co-workers were brave enough to use the applications I’d built, despite the bugs and horrible user interface. They all had good feedback, and gave it freely.

I documented everything they told me, capturing it in Jira tickets, and then sat on them for months.

Why? I’d been pulled off of internal tools to doing other application development. So instead of being able to iterate with my users, constantly improving the application, they had to wait a long time before they saw any changes. I lost what goodwill I’d had to start with, because I didn’t respond to their needs.

No Training

None of the other developers had any experience with Lisp. But instead of hosting a training session, or calling for a special code review session with Clojure as the topic, I waited for them to “come to me.”

I was certain that some of them would peek at the Clojure code, just to see what it was like, and then start asking questions. I told myself this was the natural, organic way to start spreading Clojure knowledge around the organization.

I was dead wrong. The other developers had a hard enough time deciphering PHP code from 2 years ago; they weren’t going to spend time trying to read my stuff too. Especially since it was buggy. And slow. And hard to use.

Conclusion

In the end, I should have been much more proactive if I wanted to see Clojure adopted at my company. I should have given them some training, carved out some time every week to maintain the applications, and gotten some design input from the very beginning so the tools would be easier to use. Even then, we might not have decided to switch to Clojure, but at least then it would have been an informed decision, based on well-working example applications that everyone was using.

Four Things You Should Do When Starting a New (Dev) Job

Your first 30 days with a company can be nerve-wracking. Your boss is watching your performance, trying to decide if he made the right choice. Your co-workers are trying to learn if you’re going to be a helpful member of the team or just dead weight.

All that scrutiny gives you a golden opportunity to make a great impression.

Here’s four ways to make sure they’ll be glad to have you on board:

1) Go in Early

I know a lot of developers are night owls, but for the first 30 days, you should make an exception. Come in earlier than you normally would, but stay as late as everyone else does. You’ll show everyone you’re serious about the job, and aren’t afraid of work.

Even if you shift your schedule back to a normal one after the first 30 days, you’ve gone a long way to reassure your colleagues that you’re here to help them out.

2) Update Documentation

Was the system setup process well documented? If not, fix it. If documentation doesn’t exist yet for something you’re supposed to learn, create it. Your boss will notice, and you’ll be helping future employees get up and running faster.

3) Ask Questions

It’s one thing to learn how a company does things. It’s better to learn why they do them the way they do.

Look for areas where they don’t have any reason to back up how they’re doing something. If you can find a better way of doing it, pitch it to your boss. You’ll show him you care about the quality of your code, and you won’t have to step on anyone’s toes.

4) Stay Calm

You’re learning a lot of new things, and trying to stay productive at the same time. Don’t rush yourself. Most employers don’t expect their employees to start seriously contributing until they’ve put in 90 days. You’re going to be slower than normal, but that’s ok. Take the time to really learn all the nooks and crannies of the system you’ll be working in now, so you can contribute more later.

Five Reasons to Learn a New Programming Language

You’ve landed a good job. You negotiated a high salary, plenty of vacation days, and flexible work hours.

Congratulations. But don’t stop thinking about your career just yet. Now that you know your way around your current programming language, it’s time to learn another one.

Here’s why:

1) Languages go obsolete

Every programming language becomes obsolete at some point. COBOL isn’t used for new software anymore. Whatever language you’re using now will be obsolete, or at least on the downward trend, in 5-10 years. You don’t want to end up chained to one company (or worse, laid off from said company) with only an obsolete language on your resume.

Pick a language that looks to be on the upward curve, and learn it. Think of it as insurance against the future.

2) Languages express best practices

When someone creates a new programming language, they’re codifying a certain style of programming that they consider to be the best. After all, if Guido Van Rossum thought functional programming was the best style to work in, Python would have been a functional language.

One of the easiest ways to get deep into someone else’s ideas about the best way to program is to learn their language. Consider the learning experience a conversation you’re having with the language creator, trying to learn how they think programming should be done.

You’ll probably agree with them on some things, and disagree on others. Take the things you like – the parts of the language that make some best practices easier – and find ways to incorporate them in the language you’re currently using.

3) Languages don’t do everything well

Most of the general-purpose programming languages we’ve got are actually really good at just one or two things, and terrible at others. PHP is a great scripting language, but pushing it to express a complicated DSL leads to code that’s hard to read and painful to write.

Picking up a new language gives you a larger toolbox to draw on, so when it comes time to build that internal website, or reporting system, or distributed image processing software, you’ll be able to use the best language for the job.

4) Languages lead to jobs

This one almost goes without saying. You can’t land a Ruby job without learning some Ruby. You need to know C++ (or Objective-C) to work in the games industry. If you want to switch from web development to scientific computing, learning a new language is your best bet.

5) Languages teach programming fundamentals

Do you know what a monad is? How about S-expressions? Learning a language that’s radically different from what you normally work in (say, Haskell for Python devs) can teach you new programming concepts. You’ll learn new ways of thinking about programming, and new ways to approach traditional programming problems.

In the end, learning a new language (or several languages) is all part of becoming a mature, experienced developer. If you want to prepare for the future, expand your programming horizons, and keep a secret weapon (or two) in your dev toolbox, then you should learn a new language.

10 Questions Every Developer Should Ask In Their Next Interview

With the market for developers so fierce, we can afford to be a little more careful about where we work.

Over the years, I’ve interviewed at several companies. Whenever we got to the part of the interview where they asked what questions I had for them, I had plenty of stuff to ask about their tech stack, but not as much to ask about their working environment. Here are 10 questions I wish I’d asked at every interview:

1. How many hours a day do you require from your developers?

You’d be surprised how many places expect more than 40 hours. If they want you to work 45 or 50 hours a week, it’s best to know that up front, so you can negotiate an appropriate salary.

2. Do you allow working from home? How many of your developers work from home at least once a week?

A lot of companies will say they allow working from home. If they’re actually committed to it, though, they’ll be at least one or two developers taking advantage. If no one works from home, it’s because it’s discouraged by management, or they don’t really have the infrastructure (VPN, etc) in place.

3. What is the timetable for getting vested in shares?

A critical question for startups. They’ll usually ask you to take a lower salary in exchange for shares. But if those shares take 5 years to become vested, you’re looking at a long wait for a risky payoff. Do you really want to sacrifice money in the bank for that long? Best to make an informed choice.

4. Do you have “office hours” you like your developers to keep? What are those?

Most places are flexible, but have some sort of core hours they want you to keep. These may not fit your natural schedule (I’m personally a morning person, but I know a lot of developers that prefer to code at night).

5. What’s your leave policy? Are Vacation and Sick Days lumped together? How many do you give a year?

Several startups are offering unlimited leave these days. It makes sense; if you’re a salaried employee, paid per year to get a job done, does it matter how many sick days you took?

If they won’t go unlimited, consider bargaining for increased vacation instead of a higher salary. You’ll thank yourself later.

6. What’s your process for changing internal tools – e.g., if you were to change version control systems, how would you go about that?

Over the life of any piece of software, your tools are going to change. Is this dev team ready to handle that?

7. Do you allow developers any time to work on open source projects, or just projects of their own for the company?

You’re going to want to spend time on your own stuff, whether to try out a new language or keep your skills up to snuff. You’ll probably want at least some of that to be open source work, so you can show future employers your coding prowess without violating NDAs. If your new job won’t let you invest in yourself, will they pay for training or conferences to compensate?

8. What’s your employee review process? How often do you do employee reviews?

Ideal would be reviews after 30, 60, and 90 days for new employees, with everyone else getting evaluated every 6 months. You need the feedback an employee review can give, and they should want to hear from you.

9. Do you do code reviews? How often? What’s the process?

Feedback, feedback, feedback. One of the fastest ways to grow as a developer is to have other people read and comment on your code. Frequent code reviews mean the team is committed to getting better.

10. Do you do project reviews? What’s the process? Alternate: How is the performance of the dev team as a whole measured?

Another feedback question. Again, you’re trying to see if the team is serious about getting better.

These questions won’t cover everything, but they’re areas we developers often forget to check when looking for a new job.