Suppose that we have a Ruby class called SomeClass
, and SomeClass
has an array class constant called SOME_ARRAY
. SomeClass
also has a method called same_elements?
that checks if an input array has the same elements as SOME_ARRAY
without regard to ordering. We may be tempted to write code that looks like this:
This code works, but it’s an inefficient solution. We sort SOME_ARRAY
every time same_elements?
is called, even though it’s a constant that we defined ourselves!
We could define SOME_ARRAY
as ['foo', 'bar', 'car'].sort
, and that gets us most of the way to improved performance. We only expect Rails to load our class definition for SomeClass
once when we start our application, so even if we’re creating many instances of SomeClass
(eg. once per request to a REST endpoint), we’re still only calling .sort
on SOME_ARRAY
once.
However, if we want to be even more efficient with our compute power at runtime, we can simply define SOME_ARRAY
to be sorted when we write our code, which lets us avoid calling .sort
when we load the class definition.
This revised code has even fewer calls to .sort
than when we defined SOME_ARRAY
as as ['foo', 'bar', 'car'].sort
. However, this introduces a new problem into our code: if we ever rewrite or modify SOME_ARRAY
in the future, we need to ensure that that constant remains sorted.
For example, a future commit could change SOME_ARRAY
to ['new element', 'bar', 'car', 'foo']
, which would cause a bug in our same_elements?
method. Hopefully our code review process and tests on same_elements?
would stop us from introducing that bug.
However, through RSpec, we can actually enforce that SOME_ARRAY
remains sorted. We introduce a test like so into our SomeClass
test suite:
This ensures that SOME_ARRAY
remains ordered when we introduce new elements to it. By precomputing the sorted order of SOME_ARRAY
in our testing and continuous integration environments, we’re able to do fewer .sort
computations at runtime in our application.
Note that we should only use this method when there’s no impact to SOME_ARRAY
‘s readability. If SOME_ARRAY
is defined as ['foo', 'bar', 'car']
(ie. random elements where we don’t care about the ordering), making its definition alphabetical in the source code makes sense. Using this method also makes sense when SOME_ARRAY
is something we already expect to be alphabetized (eg. ['alpha', 'bravo', 'charlie']
). However, if we expect some other sort of non-alphabetized ordering of SOME_ARRAY
, then making our code less readable in order to save compute power would be the wrong tradeoff. For example, redefining ['one', 'two, 'three']
to the sorted ['one',
‘three', 'two']
makes our code less readable, which isn’t worth the slight performance gains.
Start your journey towards writing better software, and watch this space for new content.