Preface
I applied for the 37Signals junior developer role and ended up being invited to do their technical test. The invite was sent to ~4% of applicants and I managed to get one by submitting the below.
Each section was hosted on a page belonging to a Writebook so on submission seemed much neater than it does below. Essentially, it takes the 37Signals definitions of a junior programmer per their handbook and discusses them in context of things I have already done.
Ultimately, I was not invited to the final interview stages but being top 4% after ~8 months of exposure to Ruby & one project in Rails straight out of university doesn’t strike me as bad going.
The technical test submission is on my GitHub , with pr.md
detailing some of the design choices.
Intro
Hello! Welcome to my Writebook instance, while currently bare it is going to grow into a blog I can reference elsewhere.
This particular book is intended to act in support of my application for the Junior Rails Developer role advertised by 37Signals. Throughout, I breakdown the definitions of such sourced from the handbook as I feel there is no better way to demonstrate suitability for a role than using the role definitions directly.
My website contains thorough breakdowns of my projects and experience while this document touches briefly on key points in reference to the position. Alongside this, a range of my academic writings can be found in this book.
While relatively new to the world of Rails and the social side of programming, I try and remain active in r/rails , GoRails and Ruby as I learn almost as much from reading and attempting to help other people new to the world as I do from directly teaching myself via the docs.
I have also been lucky enough to be granted an education ticket to Brighton Ruby through a grant dedicated to Noah Gibbs. I intend to use this not only to learn more about the ecosystem but to also springboard my social ties as there is no Ruby/Rails community in Birmingham, UK or the surrounding area, I intend to start one.
I look forward to hearing from the team!
Mastery
Proficient in basic language and framework features, but some advanced structures or features may still be unfamiliar. Occasional issues following patterns and approaches within existing code bases.
My project Erudite is effectively a love-letter to the basics of Rails & Hotwire based on the joy I felt building it.
Effectively, Erudite is a CRUD app with conditional access based on user types with the additions of ActionText & messaging via Turbo Streams. There is a ~12 thousand word report on the project, why it exists and how I built it available as another writebook of mine but in short, I have exposure to 3 universities. Combined, these experienced offered literally hundreds of hours spent telling my peers it was okay to email lecturers despite how strange formal email etiquette can seem at first alongside the burning sense of personal frustration at how inefficient the system is.
Got a question about the content? Okay, clarifying confusion is how learning works so:
- Take a note of the content you’re confused on
- Take a note of your notes that show your confusion
- Open Outlook
- Write an email to your academic, hope you remember the attachments and wait 3 days
- Copy the relevant bits of said response into your work & hope the key to your understanding isn’t lost in Outlooks poor search function
I built Erudite basically to see if a better way was possible and it seems there is.
Besides the implementation of vite_rails
due to two Vue components the stack is effectively vanilla Rails with “basic language and framework features” including model-level validations for most things and single-level nested routes
Though this is not to say it was without problems, see, this initial route implementation that has since been cast into the void.
Advanced Features
The above nicely brings in advanced structures and features as Message
ended up growing into a polymorphic association:
class Message < ApplicationRecord
belongs_to :user
belongs_to :messageable, polymorphic: true
has_many :mentions, dependent: :destroy
has_many :tagged_users, through: :mentions, source: :user
validates :content, presence: true
broadcasts_to ->(message) { [ message.messageable, :messages ] }, partial: "/messages/message", locals: { user: Current.user }
end
as did my Notebook class, allowing ActionText instances against multiple objects.
There is some level of exposure to other aspects too, N+1 queries & DevOps for example.
Due to a lot of Erudite being the display of cascading information to look into optimisation so there is the inclusion of features aiming to speed query times.
def index
@courses = Course.all.includes(units: :topics)
end
Erudite also held as GitHub Actions deployment via Kamal onto a DO Droplet, while not able to spin up a deployment .yml on the fly, I can create such things with secret protection from 1Password given time to do so.
Scope
Capable of working on tightly-scoped, routine problems.
My friend started a new job, in doing so he was given a single sheet of paper with a grid. Within each grid cell was a number corresponding to a shit pattern he was expected to work spanning the next 12 months.
It was effectively illegible despite the minuscule key offered at the bottom of the page.
As such I wrote a Python script that would take the rota and automatically create the Google Calendar events accordingly to save him the hassle of having to translate the Rosetta Stone to understand his schedule.
The schedule was convereted to JSON via the smartest GPT model at the time and fed into the script to avoid having to manually write it into JSON to then have it processed.
The script is visible on GitHub and while basic in function, it solves a tightly-scoped, routine problem of “how best do I track my working hours”. This little project also acted as my first exposure to API interaction via Python.
Judgement
Demonstrates curiosity in their craft, prioritizes their own development to better serve their team. Researches what’s been done in the past and why, attempts to solve problems independently.
When I was building Erudite I first had to breakdown the exist solutions explaining why/how they were suitable or not suitable in context (available in the report) and then had to document how I inteded to beat it.
At this point I was not dead set in a project with Rails as I had never properly used it as was unaware of the myriad of offerings such as ActionCable and ActionText. I was simply aware of it as an MVC framework. Curiosity led me into the getting started guide and said curiosity is still leading me into conversations and searches to grow my knowledge.
A prime, recent example is why
<div class="chat <%= Current.user message.user ? 'chat-end' : 'chat-start' %>">
would default to chat-start
if the snippet was broadcast via
class Message < ApplicationRecord
belongs_to :user
belongs_to :messageable, polymorphic: true
has_many :mentions, dependent: :destroy
has_many :tagged_users, through: :mentions, source: :user
validates :content, presence: true
broadcasts_to ->(message) { [ message.messageable, :messages ] },
partial: "/messages/message",
end
This led me on a debug chase that ended in me discovering that Current.user
would devalue itself so in the initial snippet it would always evaluate to false.
I troubleshooted by passing Current.user
as a local into the Turbo Stream and into the initial partial render and it was exclusively de-valuing when left in the partial without being enforced via a local.
This led me to investigate current_attributes.rb
and discovered:
“Abstract super class that provides a thread-isolated attributes singleton, which resets automatically before and after each request.”
I couldn’t seem to find anything online about this happening but assumed based on the above that the WS connection was on a different thread, ergo, Current
didn’t exist because Current.session
would devalue in the same way Current.user
would.
While I knew I could avoid the problem by using locals, I still wasn’t 100% on why it happened to begin with, so I consulted the Ruby discord server to discuss it and after a lot of discussion similar to the process I went on, it was put down to a thread difference.
Tooling
Has a rudimentary understanding of our tools (AppSignal, Buildkite, Kibana, Dash, Trek, Post Office etc).
Now, this one is tough but from my current understanding:
AppSignal
A monitoring platform with support for: errors, performance, hosts, dashboards, anomalies, uptime, logging, check-ins.
Based on the above I would assume AppSignal carries a focus on uptime, performance and hosts since leaving the cloud as Sentry would assist in error coverage.
BuildKite
BuildKite appears to compliment the shift away from the cloud with hybrid pipelines for in-house infrastructure compared to that of a cloud-native platform such as CircleCI.
Kibana
Used to identify trends in data, presumably to point a UX team in the direction of pages that has little interaction or to assist in driving traffic to parts of 37Signals we feel deserves more eyes on (all of it, in my opinion)
Dash
Dash seems to be a hub centralizing the above or something to that effect. My best guess based on this and this
Trek & Post Office
I’m shaping up on six here, I can find literally nothing about either of these platforms online despite a Raycast extension called Trek which permits Basecamp access but the likelihood of this being affilitated is slim to none.
To not fall victim to sunken-cost, I yield.
Ownership
Participates in programmer on-call rotation under guidance from more senior programmers, but delivering simpler resolutions end-to-end.
While I worked at the University of Birmingham the campus experienced a campus wide WiFi outage which, to a university of ~40,000 is equivalent to the apocalypse.
I was on shift at the time as a part-time casual analyst and was suddenly part of the joint-effort to diagnose, troubleshoot and ensure the relevant comms were broadcast in an attempt to avoid 1,000 phone calls with the same query. The reaction ensured those affected took the correct token.
This meant that with the guidance of those above me, I was responsible for attaching generated diagnostic reports to the relevant incident reports for review by people far senior to myself and composing emails to be distributed by the relevant department heads once proof-read by managers.
While not specific to programming, as this is something I am yet to experience, I provided the equivalent of on-call assistance for emergency situations providing the end-to-end resolutions I was capable of at the time.
Communication
Proactively communicates what they are working on, attempts to unblock themselves but asks for help when needed. Questions mostly don’t repeat.
Again, I no longer have access to the conversations held while I worked at my internship but this entailed a daily stand-up where each member of the team would briefly talk-through what they completed yesterday and what they intended to complete on the day.
One of the projects entailed implementing a lightbox library to display the images pulled from an asset manager and spent a solid hour debugging the issue to be met with minimal success. As a result, I approached another developer & asked if they’d mind taking a look at the library. Together we discovered the docs failed to mention the required inclusion of a <script>
that is not generated by default but is visible in the source code of their example implementation.
I’ve since used that library a few times & have not yet fallen victim to the same issue.
The same can be said for my Rails questioning within the Discord server I try and remain active in, there are some screenshots of initial messages below that are the product of me spending time trying to solve a problem and then asking the community for further help or to double-check my reasoning:
Planning
Prioritizes daily work against team/project needs with regular guidance. Demonstrates good time management.
Unfortunately for this, of the team projects at university I’ve been involved in I’ve typically been voted leader of the group, as such the tasks have been self-delegated.
As a blanket vouch for this quality, in the four years I’ve spent on my degree I’m yet to hand in an assignment late. This is because naturally I hate being late, to the point I wrote a Python script that would poll my Google calendar and send me a text of my day every morning via AWS Simple Notification Service.
Work is thoroughly reviewed with substantial back and forth frequently needed before merging. Reads pull requests. Makes minor contributions to the improvement of system documentation.
At the time of writing my OSS contributions are doc-based, the below are merged:
With this being next on my todo list:
I plan on adding code-based contributions to the OSS community ASAP, the absence of which is caused by a focus on university projects as opposed to a lack of intention. This was a conscious decision through which I intended to still help where I could within the community. Hence the docs contributions and CYF volunteering.
Feedback
Observes and learns from the work and behavior of others. Receives constructive feedback with a growth mindset.
Most of my feedback regarding my code has come from the community as my university priorities functionality as opposed to implementation. However, response to feedback can be seen throughout Erudite’s report with the evolutionary sections included below.
Such changes were either made as a result of feedback from my peers or from my advancement in knowledge with the understanding updating code as I go helps avoid an eventual total refactoring.
Querying refactoring the below:
namespace :admin do
resources :users
resources :audits
resources :courses do
resources :units do
resources :topics do
resources :messages
end
end
end
end
Was met with
This conversation happened in the Ruby Discord server. Pixelated as while public, the respondents have not given me explicit permission to share the screenshot
which led to the same code effectively becoming:
namespace :admin do
resources :users
resources :audits
resources :courses
resources :units
resources :topics do
member do
delete :remove_attachment
end
end
end
resources :messages
Instead, I refactored to support access via models associations so if the Course
a message belongs to is needed it’s accessible via foo.course
.
Response to feedback outside of programming
Outside of programming I find my fun in riding motorbikes. The circumstances that led to this means I’m often the least experienced on the most under-powered bike, naturally creating an atmosphere of receiving riding feedback as many of the people I ride with have done so for longer than I’ve been alive.
I can only imagine being a junior developer is akin to this, where the feedback is intended for growth as it’s a positive for the group and the individual in reciept of it.
Due to the inherent danger associated with the hobby, the feedback has sometimes been incredibly blunt and direct to the point as it has needed to be. While at first I had to consciously remind myself it was not intended with malice I no longer struggle with this.
The point of this is to say I try and put myself in situations where I am not the smartest in the room so there is always a lesson to learn, regardless of context. To do this and not be open to feedback would create a moot point.