Skip to content

Instantly share code, notes, and snippets.

@jfarmer
Last active May 6, 2023 22:55
Show Gist options
  • Star 25 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save jfarmer/d29bb09caa024e3b349f to your computer and use it in GitHub Desktop.
Save jfarmer/d29bb09caa024e3b349f to your computer and use it in GitHub Desktop.
Dev Bootcamp's Theory of Learning

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.

This will be light on theory and heavy on ActiveRecord, so if you're not familiar with SQL or Ruby it might be hard to follow. Mea culpa.

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:

  1. There is a user with the name "John" and ID #7
  2. The user with ID #7 belongs to group #20
  3. 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 students and cohorts table. The fact that Student #7 belongs to Cohort #12 is encoded in a row in the students table.

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 "foo@whatever.com" 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

Student#alumni?

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.

Remember: the map is not the territory, ceci n'est pas une pipe, the signifier is not the signified.

Signifier and Signified by Brandon Bird.

@mdbudnick
Copy link

Pur more plainly, your program has no opinion about

Put*

Great work otherwise!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment