Tommy: The Blog

Apr 20

(Source: idrawnintendo)

Jan 28

Excited to kick off CSCI E-1 tonight! @HarvardEXT

Excited to kick off CSCI E-1 tonight! @HarvardEXT

Dec 26

Fiat.

Even if you don’t believe anything about Christmas, you have to admit it’s a pretty amazing story. I’m not talking about the whole “born in a manger” thing or that little “murder all the children” bit that puts everyone in the Christmas spirit. Nope, let’s back up a bit and talk about what happened before all that hullabaloo. The prologue, the intro, the pages that can be pretty tempting to skip over when you’re looking for “the Christmas story.”

Enter Mary, who’s 14 or 15 years old. Same age as Hermione when the Goblet of Fire came to town. That’s right, if cars existed in 4 BC, Mary wouldn’t even have been able to drive one. This is to say she was pretty young. Great, got it.

Enter an angel, who tells Mary she’s going to conceive a son despite her virginity. Doesn’t matter how she finds this out, what matters is that she does find out, and this ain’t no joke. Let’s break this down a bit.

First, Mary’s pregnant. Sure, lifespans were shorter back then, yadda yadda yadda. At the end of the day, Mary’s 14 and pregnant. That puts her at less than the median age of the mothers featured on the show that taught everyone about motherhood and maturity. Scary? You bet. Does Mary have any idea how to raise a kid? Probably not. I mean, why would she? It’s not like raising a child was anywhere near her radar.

Second, Mary’s a virgin. Yeah, because that makes sense. At this point, Mary has license to be freaking out.

Finally, and perhaps most importantly, Mary’s told that she’s going to be the mother of one of the most important people in the history of the world. Doesn’t matter what you believe, it’s pretty hard to deny that Mary’s son is going to affect the lives of billions of people over the course of thousands of years. We’re not dealing with a spelling bee champ or little league all star here, we’re talking about a guy who pretty consistently lands near the top of people’s “most famous people ever” lists. Pressure much? You betcha. 

Okay, back to the story. So Mary finds all this out, and probably needs some time to process things. You know, sleep on the prospect of raising a major historical figure as a result of a phantasmagorical miracle.

Nope.

In less time than it takes Ryan Seacrest to reveal the winner of American Idol, Mary agrees without any objections whatsoever. “Hold up,” you think to yourself. “Maybe the writers of this thing just didn’t want to waste time on boring details.” Oh sure, the book brought to you by the same people to trace the entire genealogy of Jesus (ahem, twice) is interested in being concise. Seems a bit more likely that Mary just did something pretty incredible.

Mary was presented with both the challenge and opportunity of a lifetime. It’s pretty clear that the easy way out would have been to say no. At the time, many people probably would have said that backing out would have been the more reasonable thing to do. But, Mary didn’t. Instead, she ventured headstrong directly into the unknown without thinking twice about it. Maybe she didn’t fully understand what was happening or why, but it doesn’t matter*. Mary said “yes.” (Technically, “fiat.”) That’s crazy awesome. 

Takeaway time. I think the story kinda speaks for itself: we need to say “yes” more. Last semester of college means it’s the last chance for a whole lot of things (which include challenges, I’m sure). Of course, it’s not like that won’t be true for life in general, but the last of anything only comes once. So, looks like I need to follow Mary’s example by seizing crazy opportunities and taking risks. Easier said than done, but taking the easy way out is obviously… easy, but if that’s all life were, it wouldn’t be much fun.

Merry Christmas!

* That being said, I don’t think the Mother of God was a derp.

Oct 25

Lessons Learned from Launching CS50x

Last Monday, CS50x launched on edX, an online education platform hosting classes from Harvard, MIT, UC Berkeley, and UTexas. The CS50 team has developed a suite of apps to facilitate discussions among students and staff, assignment submission, and automatic grading (among other things), which also went live with Monday’s course launch.

First, a timeline of our launch day. We definitely had no idea what to expect on the first day of the course, but here’s a quick rundown of my sunny Cambridge afternoon.

Needless to say, I spent the day frantically monitoring our infrastructure, which actually held up pretty nicely. When all was said and done, we had fun Day 1 facts like:

And now, here some things we tried to keep in mind in anticipation of our launch, and some things we learned along the way.

Reduce single points of failure. If any points in your application are mission critical, make sure there’s more than one of them. For example, if you’re making a search site and your search engine service goes down on launch day, you can kiss that seed funding goodbye. Same goes for your database and application code. Instead of just running everything on one server, distribute code as much as possible across a cluster of servers, so that if one server gets struck by lightning, it’s not the end of the world. Similarly, data stores should have hot-swappable, replicated copies. If Murphy’s law kicks in and a database service dies for seemingly no reason, you don’t want to be restoring from a backup you manually made a few hours ago; you want to be ready to make a seamless, transparent swap.

Have a strategy to roll back. Inevitably, that quick fix you applied at 4am is going to wreak havoc on your production machines when you least expect it. When that happens, you don’t want users to be waiting on you to remember the difference between a git revert and a git reset. While we use git for source control, we use RPMs to version our production deployments. When we want to ship code to our servers, we do a single push to a build server, which builds an RPM containing everything from the application code to system configuration files, and propagates it out across the entire cluster. To revert to a previous version, all we have to do is install an old RPM, and we know that our entire system configuration is exactly as it was whenever we rolled out the RPM originally.

Be ready to bring up new instances. All of our apps are hosted on Amazon Web Services, which makes spinning up new servers a breeze. In anticipation of the load spike, we provisioned a cluster of EC2 servers just for the online course. However, throwing (virtually, at least) more hardware at a problem isn’t a bad short-term solution if you’re in the middle of a massive traffic spike. Being aware of when the load on a cluster is too high and being able to quickly distribute that load over a greater number of servers can reduce performance degradation during a spike. With our RPM-based approach, configuring a new server is as easy as running a yum install, and that new server will be configured exactly like the other servers.

Cache the crap out of everything. Okay fine, not everything. But a lot of things. The list of new posts on the discussion board? Cached. The replies to one of those posts? Cached. The students you can grade? Cached. The courses you’re enrolled in? Cached. With our apps (and most apps, really), every page contains tons of data that changes infrequently yet is seen by a large number of users. If 1000 users look at the same list of new posts, there’s no reason your code should be asking the database the same question 1000 times. It’s like that stupid knock-knock joke with the bananas and the orange. Not funny. We use a combination of Memcached (which is wonderful) and Redis (which is also wonderful) for these kinds of things, but pretty much anything that grabs something from RAM in O(1) is going to be faster than going to disk with a database query.

Warm caches and load balancers. When your site goes live, you want to make sure that caching layers and load balancers aren’t running on empty. While many of Amazon’s web services auto-scale beautifully, you don’t want your first users to be the ones triggering the scaling, else your site will load slowly at the outset of a traffic spike. Make sure these kinds of things are sufficiently scaled when starting out, following the same principle of making sure to have enough servers ready to go, so hopefully you don’t need to spin up too many more instances (though you’re ready to, of course).

Assume queries can return a million rows. While developing software, it’s generally easy to test on a small data set. With 5 users on your test site, everything could perform nicely. It’s not until you have a large number of users that a single badly-written query can ravage your once-shiny servers. When writing a query or designing a system, consider at least in the back of your head that it could be run on a really large data set. That means making sure indexes are created in the right places, and queries are limited as much as possible. Even better than that would be testing on a large data set before deploying to the masses.

Definitely one of the busier Mondays in recent memory!

May 31

Selenium + Test::Unit + Rake = Painless Web Testing

Selenium, Ruby unit testing, and Rake are all fantastic tools that will make your life as a developer much, much easier. Together, they can make testing web applications painless. I’ll try to go beyond simply explaining what these technologies are by outlining effective ways of using them (though if you’ve never even heard of any of these technologies, don’t worry!).

Setup

First, we’ll need to get everything set up. Once you’ve installed Ruby (the latest is of course recommended), simply run:

gem install rake selenium-webdriver

See, painless. You’ll also want to download the Selenium Server, which is just a JAR file.

Selenium

Selenium allows you to automate your web browser, which means you can use it to implemented application tests for your site. Let’s say you made a blog and want to make sure users can create posts. A basic Selenium test might look like this: log in as a test user, navigate to the “create a post” page, populate a form with a post title and some content, hit the submit button, then make sure the post has appeared.

The Selenium WebDriver is available for several languages, but we’ll be using Ruby here because Ruby is the best. Here’s what the test we just described might look like using Selenium’s Ruby WebDriver API:

require 'rubygems' 
require 'selenium-webdriver' 

# navigate to the login page 
driver = Selenium::WebDriver.for :firefox 
driver.get 'http://yourblog.com/login' 

# log in a test user 
driver.find_element(:name, 'email').send_keys 'example@example.com'
driver.find_element(:name, 'password').send_keys 'password' 
driver.find_element(:id, 'btn-login').click 

# create a post driver.get 'http://yourblog.com/post' 
driver.find_element(:name, 'title').send_keys 'post title'
driver.find_element(:name, 'content').send_keys 'here is some post content' 
driver.find_element(:id, 'btn-post').click 

# close the browser
driver.quit

Let’s walk through this. First, we require the necessary libraries. Then, we create an instance of the Selenium web driver, specifying that we want our tests to be run using the Firefox web browser. We could also have said :chrome, :ie, etc. Next, the get driver method navigates to a given URL, which in this case is our login page. The few lines of code that follow are the core of Selenium. The find_element driver method returns an instance of a DOM node matching the given selector. For example, we can find elements by their id or name, or we can supply a CSS or XPath selector. We can then perform actions, like clicking or typing, on the node by using its click or send_keys methods.

If you run this script, you should see a new Firefox window open up, run each of these steps, and close. It’s actually pretty cool to watch. What’s nice about Ruby-flavored Selenium is that it reads like English, and it’s very concise. This is just a small sample of the WebDriver API, so I’d recommend checking out the more comprehensive documentation.

But, this isn’t really a test yet. How are we supposed to know if our post was actually created or if our actions triggered an internal server error?

Test::Unit

Enter Test::Unit, Ruby’s fantastic built-in unit testing library. Here’s how we can add some validation to our test:

require 'rubygems'
require 'selenium-webdriver'
require 'test/unit'

class BlogTests < Test::Unit::TestCase
    def setup
        # create selenium objects
        @driver = Selenium::WebDriver.for :firefox
        @wait = Selenium::WebDriver::Wait.new :timeout => 10
    end

    def test_post
        # navigate to the login page 
        driver.get 'http://yourblog.com/login' 

        # log in a test user 
        driver.find_element(:name => 'email').send_keys 'example@example.com'
        driver.find_element(:name => 'password').send_keys 'password' 
        driver.find_element(:id => 'btn-login').click 

        # create a post driver.get 'http://yourblog.com/post' 
        driver.find_element(:name => 'title').send_keys 'post title'
        driver.find_element(:name => 'content').send_keys 'here is some post content' 
        driver.find_element(:id => 'btn-post').click 

        # make sure post title appeared
        assert_nothing_raised do
            @wait.until { @driver.find_element :xpath => '//h2[.="post title"]' }
            @wait.until { @driver.find_element :xpath => '//p[.="here is some post content"]' }
        end
    end

    def teardown
        @driver.quit  
    end

Alrighty, let’s walk through this. First, we’ve created a class to encapsulate all of our tests. Every method inside of the class that is prefixed with test_ will be executed as a unit test. Before each test is run, the setup method will be called, and after each test is run, the teardown method will be called. assert_nothing_raised is a Test::Unit method that will fail the currently-executing test if any exceptions are raised in the block passed to it. If unfamiliar with XPath, the query //h2[.="post title"] will find all elements on the page that look like <h2>post title</h2>.

Also new here is the Selenium Selenium::WebDriver::Wait object. Rather than attempting to find an element and failing immediately if it cannot be found, our usage of the wait object causes Selenium to repeatedly try to find an element for at most some number of seconds. In this example, when we press the “post” button, our post might not to appear immediately, as our app probably needs some time to process and display the post. So, without that wait object, our test could potentially fail even if our action actually succeeded. On the other hand, if after the 10 second timeout we specified in the setup method, Selenium still can’t find the element, then it will raise Selenium::WebDriver::Error:TimeOutError and the assert_nothing_raised will rightly cause our test to fail.

It turns out that for web applications with a dynamically-updating DOM (which, if you’re using any JavaScript at all, there’s a good chance your app is among), the wait object becomes increasingly important. For example, if your blog app loads the comments associated with a post via Ajax, then simple find_element calls are always going to fail, because Selenium isn’t going to wait for the comment to appear. Instead, you’ll need to wrap these find_element calls in a wait block.

Of course, this isn’t so concise anymore, as you end up littering your code with lines that aren’t strictly necessary. Instead, you can factor this out into a method that looks like something this:

def find hash
    assert_nothing_raised do
        wait = Selenium::WebDriver::Wait.new :timeout => 10
        wait.until { @driver.find_element hash }
    end
        
    @driver.find_element hash
end

Now, you can just say find :id => 'something' in your tests without having to worry about elements not being found (since the assert_nothing_raised will rightly cause the test to fail if an element cannot be found).

Using this same approach, we can also write a method that checks if an element is not present on the page, which could also come in handy. In this case, a wait timeout is a good thing (where it used to be a bad thing), since that means the element could not be found, so we’ll used the opposite of assert_nothing_raised:

def not_find hash
    assert_raise do
        wait = Selenium::WebDriver::Wait.new :timeout => 3
        wait.until { @driver.find_element hash }
    end
end

As you write more and more tests, a few problems begin to arise. First, each test may take a non-trivial amount of time to run, so running all of your tests when you really only care about the result of one isn’t the most productive use of your time. Additionally, your single test class will become pretty unwieldy; wouldn’t it be nice to separate functionality and factor out common logic? For example, logging in is something that you’ll likely need to do from every test suite you create. So, it makes sense to create a class or module containing these common actions, then require that file from your other tests to avoid lots of copy/pasting. Good software design should apply to tests, too!

Rake

Enter Rake, a Ruby build program that serves as a nicer version of the classic Make. Rake allows you to define tasks that can have dependencies and accept parameters, but ultimately execute some Ruby code.

Armed with Rake, we’ll be able to organize and run our tests much more effectively. First, let’s group similar tests into separate classes. For any MVC app, then it seems logical to have a test class for each model or controller. In our blog example, we can group post-related tests (like creating a post, viewing a post, editing a post, etc.) into one class, and comment-related tests (like creating a comment, realizing you’re wrong and deleting a comment, etc.) into another.

We know that to run the test suite in the file posts.rb, we just need to run ruby posts.rb. To start off, let’s create a simple Rake task to run the suite. In a file called Rakefile, add something like this:

namespace 'selenium' do
    task :posts do
        system "ruby posts.rb"
    end
end

Now, running your post-related tests is as simple as:

rake selenium:posts

Now, each test suite can be encapsulated in its own task. Keep in mind that a task can have multiple lines of code inside its block. This means that if you have any additional scripts or operations that need to be run before or after a specific test suite, you can simply add them to the task.

However, it’s likely that you have some scripts that should be run before or after each of your suites. (If instead you need something to be run after each test, then that needs to go in the unit tests themselves.) Rake’s dependency handling nicely solves this problem. We can say that the above selenium:posts tasks depends on the prior execution of another task, called clean, which can do something like clean or populate the test database. Now, our Rakefile will look something like:

task :clean do
    system "ruby clean_the_database.rb"
end

namespace 'selenium' do
    task :posts => [:clean] do
        system "ruby posts.rb"
    end
end

The new :posts => [:clean] syntax means that the clean task will be run automatically before selenium:posts is executed.

Now, let’s say you only want to run a single test within a suite rather than the entire suite. Using Test::Unit, we can invoke a single test using the command-line --name switch. For example, to run test_post inside of posts.rb, we can say

ruby posts.rb --name test_post

Let’s integrate this functionality into our Rakefile:

task :clean do
    system "ruby clean_the_database.rb"
end

namespace 'selenium' do
    task :posts, [:test_name] => [:clean] do |t, args|
        if args.test_name
            system "ruby posts.rb --name test_#{args.test_name}"
        else
            system "ruby posts.rb"
        end
    end
end

Now, we’ve added a parameter to our selenium:posts task that allows us to specify which individual test will be run. We can use our parameter like this:

rake selenium:posts[post] 

However, you may start to find that this approach of creating separate tasks for each suite to be overly annoying. So, we can take an alternative approach that creates a single task to run any of our suites:

task :clean do
    system "ruby clean_the_database.rb"
end

task :selenium, [:suite, :test_name] => [:clean] do |t, args|
    if args.suite
        if args.test_name
            system "ruby #{suite}.rb --name test_#{args.test_name}"
        else
            system "ruby #{suite}.rb"
        end
    else
        Dir.glob('*.rb') do |file|
            suite = File.basename file
            system "ruby #{suite}"
        end
    end
end

Using this Rakefile, we can say:

rake selenium

to run all of our suites, or we can say:

rake selenium[posts]

to run all of the tests in our posts suite, or we can say:

rake selenium[posts,post]

to run test_post in our posts suite.

In our first Rakefile, we had the ability to conditionally execute scripts by encapsulating suites in tasks. In our new approach, we’ll instead have to write comparisons using the args.suite and args.test_name parameters to get the ability to conditionally execute scripts.

Running Selenium From Another Machine

"Well that’s all well and good," you might say, "but I develop my web applications by SSHing to another machine." This is a detail we’ve glossed over until now, since we’re assuming that our Selenium scripts are running on a machine with a display, which isn’t the case if you’re SSHing to your EC2 or Linode instance. (Astute readers will also be wondering why they downloaded that 30 meg JAR file.)

A naive approach here would be to simply write and run the tests on your local machine. However, in order to then place these tests under version control along with the rest of your app, you’ll need to keep your local scripts and the remote scripts synchronized. This, of course, is a huge pain and can potentially lead to data loss.

Instead, we can use SSH reverse tunneling to allow communication from remote Selenium scripts to local machines. If you normally connect to your virtual machine using something like:

ssh user@example.com

then to establish a reverse tunnel, you’ll use something like:

ssh -R 4443:127.0.0.1:4444 user@example.com

The -R flag means we want to establish a reverse tunnel from the remote machine to the local machine. Now, when the remote machine sends requests to 127.0.0.1:4443, they will actually be forwarded to the local machine on port 4444.

Okay, now we need to get our local machine ready to receive Selenium instructions. To do so, simply execute:

java -jar selenium-server-standalone.jar

where selenium-server-standalone.jar is the JAR file you downloaded while setting up. That JAR will output a bunch of stuff while starting up, but near the bottom you should see something like:

Started SocketListener on 0.0.0.0:4444

Now, a Jetty server will be listening on port 4444, ready to receive instructions. That’s why we chose port 4444 in the ssh -R command listed above.

Finally, we’ll need to modify our Selenium tests to connect to our local machine. In our previous setup method, we created a Selenium web driver instance like this:

driver = Selenium::WebDriver.for :firefox

Now, we’ll create the Selenium driver like this:

driver = Selenium::WebDriver.for(
    :remote,
    :url => "http://localhost:4443/wd/hub"
    :desired_capabilities => :chrome
)

Notice here that we’re specifying port 4443 so that all requests will be forwarded to the local client, and that URL is the one outputted when we ran the Selenium server. Now, when we run our Rake tasks from the remote server, a browser will be opened up on our local machine.

There are also ways of running Selenium in headless mode (that is, not requiring a display). But, this post is already long enough, so that’s a topic for another day.

Testing is critical to developing reliable and correct applications. Selenium’s frontend testing is only one piece of the puzzle, but it’s a great start.

May 10

A Response to TechCrunch’s Assessment of edX

I recently read this TechCrunch article: http://techcrunch.com/2012/05/09/move-over-harvard-and-mit-stanford-has-the-real-revolution-in-education/, and I wanted to share my thoughts. (These were originally posted as a comment on the article, and are reproduced below.)

This article is unfortunately inaccurate. As other commenters have pointed out, edX will not simply be a repository of static lecture videos. In fact, the very features mentioned by the author, namely “automated quizzes, wiki-style forums, and a tailored assessment of progress,” show that this is not the case. The mere fact that students can receive feedback on assignments and interact with others in a curated forum shows that edX is much more than a simple extension of OpenCourseWare.

The inaccuracy of the reporting of this article is reflected in statements like, “saying that EdX is ‘the biggest change in education since the invention of the printing press.’” That is quite frankly not what Professor Agrawal said. The full context of the quote, taken from MIT’s announcement, shows that Professor Agrawal stated online education, not the particular implementation of edX itself, was revolutionary: “The new possibilities afforded by today’s technology, he said, have created ‘the biggest change in education since the invention of the printing press” (http://web.mit.edu/newsoffice/2012/edx-launched-0502.html).

It certainly seems like the author has not enrolled in or even looked at MITx’s current course offering. The MIT announcement has made it clear that this will be the model other edX courses will offer: “The technological platform recently established by MITx, which will serve as the foundation for the new learning system, was designed to offer online versions of MIT courses featuring video lesson segments [i.e., not merely traditional lectures], embedded quizzes, immediate feedback, student-ranked questions and answers [i.e., the supportive community the author claims will be necessary], online laboratories [i.e., the active problem-based learning the author claims will be absent from edX] and student-paced learning” (http://web.mit.edu/press/2012/mit-harvard-edx-announcement.html). This is fundamentally different than OpenCourseWare, and this is exactly why edX is shifting away, not towards, 90 minute lectures that passively engage students.

I completely agree that Stanford is contributing to a new revolution in education. However, their innovations by no means exclude other universities from changing the way they approach online education. To say that Harvard and MIT are not innovating with their new partnership is to misunderstand the very mission of edX.

May 02

Why Harvard and MIT’s edX is a huge deal.

Today, Harvard and MIT announced edX, a joint initiative to make online courses available to the global community through an open platform. The press release is here: http://web.mit.edu/newsoffice/2012/mit-harvard-edx-announcement-050212.html, and some great commentary by the New York Times is already available here: http://www.nytimes.com/2012/05/03/education/harvard-and-mit-team-up-to-offer-free-online-courses.html. I’d like to offer some observations and opinions from the perspective of a student of a participating institution.

This new partnership is a seriously huge deal, particularly coming from research institutions steeped in tradition. Harvard and MIT certainly aren’t the first institutions to bring learning online, as organizations like Khan Academy, Stanford, Coursera, and Udacity have already begun to deliver an amazing online educational experience. The incredible potential for this new venture lies in its collaborative organization. Working together, Harvard and MIT will be able to deliver a better experience than either of the institutions could produce on their own, and tremendous value will come from discussions among the experienced educators of both universities. Perhaps more importantly, though, is the inter-departmental collaboration that will supplement the cross-institutional cooperation. Students will be able to learn from minds specializing in a wide variety of fields, from the natural sciences to the humanities to the social sciences. By centralizing this immense breadth of knowledge in a single online platform, students and instructors alike will be able to learn from each other in order to create a revolutionary new way to learn. Whenever individuals come together for a common good—-in this case, whether that be to develop edX or to participate in its courses—-there’s potential for something to become a huge deal.

edX genuinely seeks to improve education for students. This initiative does not exist to generate more revenue for these universities or to serve as a bullet point on an admissions brochure. Rather, Harvard and MIT are poised to carry out one of the most historic educational research studies the world has ever seen. Not only will students learn new skills and ideas from their instructors (and perhaps more than ever, from their peers), but massive participation in the open learning platform will enhance future generations’ classroom experience—-whether that be online or in-person. It’s a win-win for everyone, and it’s a huge deal.

To accomplish these goals, edX has not set out to be simply a network of static content. I find it hard to believe that Harvard and MIT have invested $60 million to host lecture notes online. Instead, these institutions have dedicated significant resources to rethinking what a modern online education should look like and how platforms can effectively engage with students. This investment suggests that the philosophy behind edX, open learning accessible to everyone, is quickly becoming an integral part of these universities’ missions, and that’s a huge deal.

The ridiculously low admissions rates of these universities and others, in my opinion, has long necessitated the launch of programs like edX. To keep such a wide breadth of knowledge contained at this level and to exclude individuals with a genuine interest in advancing their own education from expanding their horizons is to perform a disservice to instructors and students worldwide. Harvard and MIT have made it clear that they will welcome collaboration with other interested institutions, and I sincerely hope that this announcement is the beginning of a larger trend of making high-quality eduction a more fundamental and accessible aspect of the lives of individuals around the world. That, my friends, would be a pretty huge deal.

I can’t wait for the fall semester.

Apr 22

I hate the word “BS”

Don’t worry, I’m not talking about a Bachelor of Science degree (or even a Bachelor of Surgery, which is apparently a thing according to Wikipedia). BS here stands for “bullshit,” and around a college campus, it’s frequently the word used to describe how you spent your all-nighter. “I just BS-ed that response paper.” “I just BS-ed the last few pages of my term paper.” I hate it when people say that.

Maybe that’s why I’m a CS major. You can’t just BS code; that’s just not how it works. Either your code is correct or it isn’t. When I ask your program what 2+2 is, it either tells me that it’s 4 or you’re not getting an A. Perhaps contrary to popular belief, that’s how proofs (not limited to CS here) work too. Either you’ve convinced me something is true, or you’ve missing something / said something that’s incorrect.

Before I continue, let me make one thing clear: this is NOT a “sciences are harder / better / faster / stronger than the humanities” rant. Maybe you think that’s true or maybe you don’t, doesn’t really matter to me. Given that 100% of my courses this term require essay-writing, I’m not here to claim that the humanities are pointless and stupid, because they’re not.

In fact, there’s a lot of value in writing an essay about a literary work or scientific article. Writing a good essay requires a great deal of critical thinking and analysis. You can’t just take things at face value or repeat what other people have said. Instead, you need to be skeptical and think about the issues at hand yourself in order to reach some conclusion. At the end of the day, you may end up with an essay about Shakespeare that adds literally nothing to the literary analysis community. In fact, that’s probably what a vast majority of essays written for school do anyway. If they didn’t, then we’d have a lot more professors than students. In that sense, writing an essay isn’t so much about the final product than it is about the process. It doesn’t matter that you’re repeating what someone has already said if you’ve gone through the process of discovery yourself. Writing an essay and giving its subject (in whatever domain that may be) critical thought has made you that much better at analyzing and assessing ideas in general. Challenging established authority and standards is critical to innovation in any field. Hell, if Columbus believed that the world were flat, I’d be sitting here writing this in Austria with a nice Vienna sausage at my side. I’m not planning to pursue a career in essay-writing, but the ability to think critically is necessary even to being a good citizen.

Saying that you “BS-ed” an essay completely devalues the critical thinking process and thus the entire point of writing the essay in the first place. BS-ing something by definition means you didn’t give it much thought at all, you just put something on paper. Sure, that could be enough to convince your grader that you deserve an A, but did you really learn anything from doing so? Just like the essay, learning is not so much about the finished product than it is about the journey there. You could buy a degree as easily as the Yankees can buy a World Series, but is that really as satisfying as undertaking the process yourself? Seems to me like too much of college is doing whatever it takes to get good grades at the expense of not really learning anything at all. BS-ing an essay is a complete waste of time, and it certainly doesn’t facilitate learning in its purest form.

"But I’m a busy student," you say. "I’m taking five classes, I have a part-time job, and I’m a member of 93 clubs this semester," you say. I know. That’s why the widespread phenomenon of BS isn’t entirely your fault (but let’s be real, sometimes it’s completely your fault, I look at lolcats as much as you do, if not more).

I posit that the number of papers that are BS-ed is very much proportional to the number of BS-able papers that are assigned. Crazy, right? I can’t tell you the number of times I’ve completed a stupid assignment requiring no critical thought whatsoever, one that was simply assigned so that the instructor could have something to assess. So it’s totally the fault of the instructor, right? Nope, still wrong. Would you like to have your entire semester’s grade based on a single analytical essay? Didn’t think so. The way higher education is set up right now, students need to be evaluated on some tangible product(s), and that constraint alone is going to lead to BS assignments and BS responses to them.

The only solution here is for individuals at every level of the academic hierarchy to re-evaluate what it means to learn something. The ~$200k that goes towards a college education isn’t for printing that piece of paper you get at graduation, it’s for learning.

So, stop BS-ing papers. Stop assigning papers you know will be BS-ed.

Try to learn something.

Feb 22

ryanbrenner:

(Note: This is a .GIF, so view in High-Res for maximum lol’s)

Team Bowden represent.

ryanbrenner:

(Note: This is a .GIF, so view in High-Res for maximum lol’s)

Team Bowden represent.

Feb 14

Happy Valentines Day, Julia!