Rails is great because it’s so easy. Rails is awful because it’s so easy. The same thing which
makes that framework so good for developers, especially for beginners, makes it so bad
for real life programming. The point is that a framework is not all. Tens of years of
software development didn’t came up with just learning a framework to develop great
products. Lot of decisions which Rails made for us is fine in most cases, but makes lot of developers
unaware that there is more than just a framework their are using now and eventually they will
work with another stack.
Below I’m listing skills that got lost or developers aren’t paying much attention
mastering those. To be honest not all were lost due to the Rails itself, some are related
to Ruby language and ecosystem.
Watching Rails logs makes me cry blood. There is a reason why events should be logged
in just one line. It is because of the parsability. Repeat it: logs should be
optimized for parsing not reading. Well to be exact, readability is important in
development, but production logs should always be easily parsable.
I saw developers that care about indentation and line wrapping when logging some
JSON/XML responses. I was wondering why they didn’t add syntax highlighting.
Logs format isn’t even the biggest problem, as changing it is quite easy (eg. with
this gem). The biggest problem is that developers don’t know when and what to log.
Many times I saw logging of completely meaningless information whereas important things
like 3rd party requests, parameters, response (codes / message) where completely
ignored. Logging all those events is invaluable when debugging production running
applicationa and in any post-mortem crash analysis. In most cases however, I’m missing
those information in app logs and need to rely on my intuition.
Obiect Oriented Programming
Ruby is object oriented language. It’s a pitty that so few developers really spent
time learning principles of object oriented programming. Developers don’t care about
proper abstractions, encapsulation, coupling. They just hack out the solution. In most cases
the excuse is that the code is doing what it should.
Good code not only does what it should. Good code is readable,
testable, maintainable and extendable. Note that I’ve put readability first. Code is read more
often that it is written. Source code serves as one and only documentation (comments or
word documents lie!).
There is a trend in Rails and Ruby to develop with the most sophisticated techniques
developer can use. Convenience is used before the readability. I believe it should be
the opposite. Convenience is good whey you’re writing a framework or a library. In real
life coding readability is more important as at some point you or someone else will come back to things
that have been implemented and would need to refactor it.
Lately we see huge conversion towards good object oriented programming. We see
programmers implementing lightweight, single-purpose, stateless services. That’s good, but
single responsibility is not the only SOLID principle. Now it’s time to
learn Rails developers to write code that is open for extension, but closed for modification.
SQL and DB modeling
ORM did lot of good for software development, and about the same amount of damage.
The damage is increased by the solutions like Rails migrations where database schema is
build automagically with bunch of some method calls and rake tasks.
So whats the problem if ORM and migrations are so convenient? The problem is the convenience
and laziness of software developers which do not verify that generated schema is actually
correct (or efficient). In most cases, especially in Rails schema is missing indices. Indices are missing
for obvious places like foreign keys, but also for columns which are searched or ordered by,
though those are less obvious places.
Similar thing with querying the database. Developers do not verify the SQL queries leaving
the ones generated by the framework. In most cases it is fine, but there are so typical
cases where developer should verify the query for things like n+1 problem. So much time
I see typical places which screams for
Problem with database modeling is especially visible when using NoSQL databases, where
developers tend to put lot of garbage and just do not care. At some point however this
mess hits back and hits very hard.
Design and Architecture
Every Rails developer knows what MVC acronym means. Less will know the origins and what
was the original purpose of this pattern. Even less will know anything other than MVC.
With DHH advocating for removing layers and indirections the Rails community came with flat
applications, which are fine for small to mid size. For anything more complex
it isn’t enough though. Flat architecture Rails is promoting just doesn’t work well
for complex apps. We end with god classes, callback hell, duplicated logic and complicated tests
(if any as such cluttered code is untestable in most cases).
The point is, that framework should be an implementation detail, not the application itself.
In Rails it is the opposite. It works well at the beginning, but in complex scenarios
you need to fight against the decisions that framework developers made for you. Despite what DHH
is saying adding layers and indirections is not bad idea. It’s neither bad nor good. It’s
contextual and developers should decide given the context of their application.
Rails developers tend to not see any other way of implementing app that they were teached
while learning the framework. When you talk about design patterns or DDD they will call you some
Java zealot. However the Rails Way is only one of many approaches and the problem should
determine the solution not the opposite.
That point may be controversial, as everybody nowadays is TDD-ing, BDD-ing, CI-ing.
The problem is that despite code has tests it is poorly tested. Tests,
if testing anything at all, completely skip testing edge/corner cases. Those are
the real evaluation whether the piece of code is working correctly or not. In most cases tests
are focused to sanity check some success paths and nothing else.
This problem especially escalated after DHH was advocating against the unit testing
in respect of integration tests with full stack involved. Integraion
tests are time consuming and requires lot of code to be written in order to be able to execute, therefore
developers start testing in more general levels and in least scenarios. Also when you’re
doing integration test it is harder to simulate edge case than it is in unit test.
Another problem with tests is that developers do not listen to them (even when TDD-ing).
The setup pain screams for refactor, but developers tends to leave the code as is because
it is doing what it should. Lot of time I see developers do not even start with red test
not understanding why it is important to ensure that test is failing for a right reason.
The most typical issue with I/O I’m encountering is HTTP file downloading. For some
reason developers tend to download the whole file, read its whole body into memory and than
save it as an output files. All of that without any reflection. Developers tend to not care
about the memory consumption. Streams and pipes are complete abstraction and so low level
that not even worth thinking about.
There are even libraries to popular cloud storage services which provide no other way of
downloading a file than to read its whole content into the memory, unless one implement
raw client to that service.
With all the high-level abstractions over the I/O developers tend to not understand nor care
what is happening at low level. The concepts of streams, pipes or non-blocking I/O seems to be complete magic,
not even speaking about system processes, threading or concurrency (which probably should be a 9th lost skill in this post).
The virtualization made developer careless about the resources, but not always you have
the option to deploy on Heroku or Amazon or infinitely scale-out. Virtualization will help
in most cases but there will be few where careless resource management will hit back.
Ruby is a scripting language that works well with the UNIX shell. Developers tend to forgot
about that. Also lot of things are solved by the shell tools yet developers tend to be
reinventing the wheel because their coding Ruby!
For some reason developers don’t know how to embrace the power of UNIX shell. Commands are
badly executed, developers don’t care whether messages are logged to STDIN or STDERR yet
lot of important debug information is just thrown away. Commands are assumed to be run successful
despite of the returned status code.
UNIX shell is developed for more years then most of developers live. Lot of common tasks
can be solved just by using what shell is providing. Learning the shell should be one of the
most important skills to acquire, but it is not as impressive as showcasing asynchronous bidirectional
It is a common belief that shell is for devops. I believe this belief is not true.
Last but not least, though maybe least important from the above set of skills. It is also least related
to Rails, more to the richness of Ruby standard library and gems.
The thing is that developers given with all typical algorithmic problems already solved and provided as easy to use plugins,
do not know or care about how they are solved. Also they find difficult solving certain
class of problems in an efficient way (efficient from the CPU and memory usage point of
view). They just hack the solution with bunch of
I know, I know. Currently we have wells of RAM and almost unlimited CPU power, especially in
web development due to the virtualization, yet
there are cases (like mobile devices) where resources matter (and battery consumption).
I’m not saying that developers should implement all the lists or sorting on they own,
but they should stop claiming that it is not their concern which should be solved by the
platform / framework they are using. From time to time they will find
a problem which they will need to solve with kind of algorithm or data structure, or maybe some
implementation will misbehave in given context and they should be able to identify such
case and know the alternate implementations.
Currently developers tend to don’t care at all in learning algorithms and data structures.
It sounds like something for long-beard geeks.
I understand that nowadays the code must be developed rapidly. Thats how the state of development is in current
market. But rapid development combined with frameworks embracing magic and undereducated
developers is a ticking bomb. The urgent need for software developers forced us to cut
corners in their education. Problem is that those education gaps aren’t filled over time.
Frameworks like Rails, that abstracted away lot of concerns, are helping
novice developers in being productive quickly, but for a cost of damaging their knowledge.
Every Rails developer should be aware of that and gradually learn things he weren’t told
in his Rails course.
Software development is not just solving all problems with the same hammer developer was
given. Improvement is not only learning new frameworks. At some point every developer should
learn the raw basics. It’s even more important as that basics spans across every platform
or framework - just implemented using different keywords.