How Dev Bootcamp Teaches: ActiveRecord
I'm Jesse, one of the co-founders of Dev Bootcamp, and the acting curricular editor-in-chief. We get lots of questions about how Dev Bootcamp approaches teaching, what our curriculum is like, and how it differs from other schools and competitors. I thought I'd share some of that with you, starting with a brief overview of our theory of learning and then sharing our introduction to ActiveRecord.
Dev Bootcamp's Theory of Learning
At Dev Bootcamp, we believe that "modeling" is central to learning. The most effective students have a clear model of how the world works and are able to quickly integrate new information into that model. A clear model isn't necessarily accurate, but it is precise. The more precise their model of the world the more easily they can incorporate new information, whether that information corroborates or contradicts that model.
A student with a muddy mental model of the world might not even realize that a new piece of information contradicts something they already "know" until someone points out the contradiction. Symptoms of a muddy model include guessing and checking, copying and pasting code and being confused that it "doesn't do what I want," and failing to consider alternative approaches or even that there could be alternative approaches.
It might sound counterintuitive, but we spend a large amount of time emphasizing and coaching students through this meta-process. You can think of it like a savings account. If the student is the savings account and the thing being deposited is knowledge, we have two parameters: the knowledge-balance and the knowledge-APY. Most conversations about student readiness, curricular and pedagogical effectiveness, and employability focus on the knowledge-balance.
We think the knowledge-APY — the return on every knowledge-dollar invested — is the strongest sign of both a student's ability to learn and their ability to thrive as a junior software engineer after Dev Bootcamp. You're free to disagree, of course. We're not making any claim that this is the Truth™ about how people learn, but this is our current model of learning (whoa, meta!).
Here's an actual example of some of our curriculum; it hasn't been edited for this blog post and illustrates how the above principles look in practice. For context, when students get to ActiveRecord in Dev Bootcamp they've already spent a few hundred hours writing command-line Ruby programs, designing database schemas, and writing "manual" code to interact with the database via Ruby's SQLite3 library.
If SQLandia were a country, students would be able to order food at a café and competently ask for directions to the nearest restroom.
What is ActiveRecord?
When anything, from the meanest thing upwards, is attractive or serviceable or an object of affection, remember always to say to yourself, 'What is its nature?' If you are fond of a jug, say you are fond of a jug; then you will not be disturbed if it be broken. If you kiss your child or your wife, say to yourself that you are kissing a human being, for then if death strikes it you will not be disturbed.
- Enchiridion of Epictetus
That we might not be afraid, let's name ActiveRecord's nature. ActiveRecord is a Ruby library that generates SQL queries, sends them to the database, and returns their results as Ruby objects.
If you're confused about what ActiveRecord is doing, don't be afraid to ask "What SQL is it generating?" At the end of it all ActiveRecord is never doing anything more complicated than that.
It's a Scooby Doo villain; underneath the scary costume is an old man trying to keep his art-smuggling ring secret. This is the most important part to remember. Don't feel guilty or stupid or ashamed that ActiveRecord is confounding you — it's a complicated piece of software, but its ultimate output is something you already understand.
The Land of Facts, the Land of SQL, and the Land of Ruby
Our software has state. You hear that all the time but what does it mean? State is a collection of facts about the world your software inhabits, and usually it's restricted to the set of facts that your software can access.
State can be anything from mundane facts like "the value of the variable
age after this program has been run" or "the value of the byte at memory address
0x007fa942260640" to "the probability that a new student will graduate given what we know about their demographic and socioeconomic background."
Many facts can be expressed with statements like:
- There is a user with the name "John" and ID #7
- The user with ID #7 belongs to group #20
- There is a group with the name "Aspiring Developers" and ID #20
Where do these facts live and how do we model or "encode" them in software? Relational databases are one answer to that question. Some facts are encoded in the structure (or schema) of the database, others in the actual data (rows) in the database.
For example, the fact that "A student has a first name" is encoded in the schema. The fact that Student #7 has a first name of "Alex" is encoded as a row.
The fact that cohort can have multiple students is encoded in the schema via the relationship between the
cohorts table. The fact that Student #7 belongs to Cohort #12 is encoded in a row in the
A Map of the Territory
Here's a high-level view of how Fact Land, SQL Land, and Ruby Land relate to each other. Most of the time Ruby Land interacts with Fact Land by going through SQL Land. Because SQL is so different from most general-purpose programming languages, we build technologies called Object-relational mappers to make it easier for us to interact with the database in SQL Land.
ActiveRecord is an example of an ORM. It's job is to translate our Ruby-ese into SQL-ese. The details of how Fact Land/SQL Land and Ruby Land/SQL Land interact are below.
Fact Land, SQL Land
Here's one possible mapping between Fact Land and SQL Land.
Fact Land SQL Land ---------------------------------------------------------------------------------------------------- There is a type of thing called a "student" We have a students table Particular students can have an id and name The students table has an id field and a name field Students are uniquely identified by their ID "id" is the primary key of the students table There is a student named "John" with ID #7 The row (7, "John") is in our students table There is a type of thing called a "cohort" We have a cohorts table Particular cohorts have an id and a name The cohorts table has an id field and a name field Cohorts are uniquely identified by their ID cohorts.id is the primary key Cohort #2 has the name "Busy Beavers" (2, "Busy Beavers") is a row in the cohorts table Students can belong to at most one cohort There is a cohort_id in the students table and students.cohort_id is a foreign key into cohorts.id Student #7 is a member of Cohort #2 The row (7, "John", 2) is in our students table Student #8, named "Alex", belongs to no cohort The row (8, "Alex", NULL) is in our students table Or perhaps.... Every student must belong to a cohort The students.cohort_id has a NOT NULL attribute Give me all students whose name starts with A SELECT * FROM students WHERE name LIKE 'A%'
SQL Land, Ruby Land
Here's one possible mapping between SQL Land and Ruby Land. There are others. This particular mapping is the more-or-less the one that ActiveRecord chooses to implement, so we'll focus on that.
A piece of software that implements any particular mapping between SQL Land and Ruby Land is called an Object-Relational Mapper or ORM. ActiveRecord is an example of an ORM in Ruby, although there are others like Sequel and DataMapper
It's worth pointing out that the mapping which DataMapper implements is not the same as the one below — don't worry about that now, though, and focus 100% on ActiveRecord.
SQL Land Ruby Land ----------------------------------------------------------------------------------- tables classes columns attributes rows instances of classes, objects SELECT Student.select(:first_name) "SELECT first_name FROM students" INSERT Student.create(:first_name => "Hey", ...) student = Student.new(...) student.save UPDATE Student.where('birthdate < ?', 100.year.ago). update_all(:retired => true) # N + 1 bug! students = Student.where('birthdate < ?', 100.year.ago) students.each do |student| student.retired = true student.save end WHERE Student.where(:first_name => 'Steve', :last_name => 'Rogers') ORDER BY (ASC) Student.order(:birthdate) ORDER BY (DESC) Student.order('birthdate DESC') Student.order(:birthdate).reverse_order JOIN Student.includes(:cohort)
Not all facts are encoded in your database. For example, the fact that an email must look like "firstname.lastname@example.org" is probably encoded in your Ruby code via an ActveRecord validation. The fact that alumni are precisely the set of students returned by the query
SELECT * FROM students WHERE graduation_date < NOW()
and that the predicate
graduation_date < NOW() is what determines whether a student is an alumni might be encoded in a handful of Ruby objects (nouns) and methods (verbs) like
class Student < ActiveRecord::Base # Returns all alumni def self.alumni where('graduation_date < ?', Time.zone.now) end # Returns true if this student is an alumni, false otherwise def alumni? self.graduation_date < Time.zone.now end end
Maps and Territories
It's important to realize that these facts don't live in your Ruby code or database, they're just encoded (or represented) there. This is no different than the fact that the symbol "5" is not actually five, but an encoding (representation) of our idea of five-ness. Humans have lots of ways to represent the idea of five-ness: "V", "5", "five", "fünf¨, "五", 101 (in binary), "|||||", etc. Or even when you ask a child "How old are you?" and they hold up five fingers, declaring, "This many!"
Each representation has its own uses. Tally marks are great if you're trying to keep track of how many people are arriving at your party but terrible for arithmetic.
Put more plainly, your program has no opinion about the fact that you chose to relate the SQL predicate
graduation_date < NOW()
to a Ruby method
That's only meaningful to a human, who has access to facts that live outside your software, e.g., the expectations of customers or idioms of the English language. As people we could interact with Fact Land to try to answer the question, "Why do we care whether a student is an alumni?" Good luck encoding that process in software.