8 Lost Skills With Rails

June 29, 2015

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.

Logging

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 includes statement.

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.

Testing

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.

I/O

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.

UNIX Shell

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 JavaScript communication.

It is a common belief that shell is for devops. I believe this belief is not true.

Algorithmics

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 if's and while's. 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.

Conclusion

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.

blog comments powered by Disqus