Ruby (on Rails) ecosystem bittersweet or "we like to hate PHP"
May 30, 2016, 12:01:22 PMtl;dr;
I am putting some facts and personal experience to prove that PHP has healthier, more competitive and loosely-coupled ecosystem, than Ruby. I am talking about Performance, Syntax and Coding aspects, Community and Tools support.
If you want to skip a lot of empty rant - scroll till the "The main part goes here..." heading :) But I need to say all of this...
What? Yet another anti-RoR rant?
Yep, I started to gather materials for this posts more than 6 months ago, and I know about all that discussions that are going around RoR's pros and cons recent weeks. The main are "My time with Rails is up" and Rails has won: The Elephant in the Room. I am definitely not so experienced in RoR to say something new, that hasn't been said yet in terms of architecture. I just want to argue on Rails leadership in web development.
[PHP] ... fragmented, independent and isolated communities ... is one big ocean of disconnected islands.
Says AkitaOnRails, and as always that's true from one side, but not true - from another. Dear Rubyists let me show you the world of the closest competitor - PHP. And there is nothing special in Ruby for "Basecamp-like" app, that we can't find in PHP.
The evolutions of my vision ...
I got to Ruby world only about 1.5 years ago, after more than 5 years in PHP development. I was struggling a lot. Not an easy migration and adoption. Due to some life chance a was chosen to build Ruby (on Rails) dev team in MobiDev, from scratch. Since that time I am involved in a thunderstorm of learning myself and bringing up to speed junior and middle Ruby developers.
Very soon I have realized one thing: those hipster and cool-kids image of RoR world was a little exaggerated. Today RoR does not propose anything better that an average PHP framework except background jobs out of the box.
Even worse - it encourages some code approaches that was "considered harmful"(C) even in PHP. Mostly violations of SRP and CQRS encouraged by the framework. And honestly quite big RoR app is a total magic, due to Monkey Patching and implicit inclusion allover.
Just think about one SRP example...
Single Responsibility Princible
— Andrzej Krzywda (@andrzejkrzywda) May 27, 2016
(by ActiveRecord) pic.twitter.com/qvX7tSZNUL
"Didn’t we stop doing PHP because of that?"
Sorry Nick, I quoted your book, and disagree with that statement. I see that a lot of Ruby developers like to hate PHP. Most of them haven't ever saw any piece of real PHP code, but "my friend have told me... what a nightmare". Or they have seen a Wordpress - the most popular and the ugliest PHP code architecture ever.
I want to state that it is a wrong meaning in 2016. And PHP community has lot of things that Ruby community is missing nowadays.
Only recently I've got this clever idea, that really seems to be true (for me).
Spot on! "In the Ruby world the second generation of frameworks never showed up (...)" https://t.co/jfHN8fkMDy
— Piotr Solnica (@_solnic_) May 25, 2016
And sorry, but before really getting to PHP, I must put these two Ruby-rhetoric paragraphs...
It's like a domestic violence...
When I think about Rails and the reaction of most Rubyists on a suggestion to "think outside of the RoR" it reminds me mind-flow of a domestic violence victim. And kind-a Stockholm Syndrome of RoR addiction.
Imagine this sad, but true story. "Cinderella" lives in cold, scary and complicated as hell world. She fall in love with a Charming Prince. He is awesome, understands her from the first letters of the word, makes her life so bright, promises to love her forever and do anything she asks...
They got married and moved to his castle. And her life changes a bit...
After years living together she can't do a step without His permission, He could even beat her if she tries. She should report every step and confirm everything with His lifestyle. She has already forgot how to live without his over-guardianship and assistance of his servants. All the people she knows and talks with - are the same wives of the neighborhood tyrants. And more and more often she hears from her "Charming": "Don't even think to leave me! No one needs you except me. You can't do nothing alone!".
Vicious circle of demand
Nothing, is the answer. There’s nothing programmatically in Ruby to stop you using its sharp knives to cut ties with reason. We enforce such good senses by convention, by nudges, and through education. Not by banning sharp knives from the kitchen and insisting everyone use spoons to slice tomatoes.
© DHH, "Provide sharp knives"
Education. Does Rails really teaches us?
Developers start using Rails. Learn to be Rails-only. Can't make a step without it. Can't distinguish what is pure Ruby and what is Rails's monkey-patching. They can't see anything better and they do not want anything better. Can't make even tiny SQL query without ActiveRecord. Performance? Who cares? Customers are caught on Rails marketing and "wide developers community". They demand apps to be done with Rails. Developers use Rails because it is so easy (not simple!) and Customers also want exactly Rails (demand creates supply). As soon as you think about leaving Rails - you have nowhere to go. Customers afraid to do non-Rails Ruby apps, because they "would not find non-Rails developers". And developers afraid to leave Rails as "they would not find non-Rails job". Vicious circle. And it is already does not matter "supply creates its own demand" or vice versa.
There is no such an issue with PHP. Customers almost never demand a specific framework. Developers are free to choose/propose the one you they know better and that suits better to particular demands. Yes there a lot of Rais-like frameworks, but there are also a number of frameworks with different philosophy,
╰( •̀ε•́╰) The main part goes here...
Evolution and Performance
In 2005 PHP hits version 5.0 and OOP approaches just became usable (since version 3.0 introduction it was very limited OOP ability). RoR is definitely a successor here. And PHP could not show anything competitive enough for a long time.
But since 2012 PHP receives a massive push with version 5.4, HipHop VM from Facebook and rise of ecosystem. Today with version 7.0 and HackLang from Facebook, PHP get 2x performance boost and lower memory consumption. It is definitely not slower that Ruby 2.x.
The biggest advantage of PHP web app is that code performance does not depend on Dev/Prod env. It does not require to reload classes (as each new request uses actual code). Only debug extension gives some penalty. But Prod env would not throw you unexpected behavior, because normally there is no any difference.
The future of PHP as a language is clear, because it has corporations behind it - Zend, Facebook etc. They are constantly working to make PHP better and faster.
What about Ruby? A lot of alternate implementations with vague prospects.
Community
Despite the corporations that contribute to PHP by itself there is a real community - PHP FIG (Framework Interop Group).
The idea behind the group is for project representatives to talk about the commonalities between our projects and find ways we can work together. Our main audience is each other, but we’re very aware that the rest of the PHP community is watching. If other folks want to adopt what we’re doing they are welcome to do so, but that is not the aim. Nobody in the group wants to tell you, as a programmer, how to build your application.
And these guys from a lot of influent PHP projects are collaborating (yes, quarrel very loud sometimes) and proposing a well thought PHP Standard Recommendations. And community really adopts them in some time.
Thanks to PHP FIG we have very important things like Coding Standard and Namespaces Autoloading Standard. It is hard to overestimate how important these things are for modern PHP. It really makes possible to combine different projects and libraries in similar, non-conflicting codebase.
Really I don't see anything like this in Ruby, except Rails dictatorship.
Packages
Yes, Bundler and Gems were amazing for Ruby and PHP manual copy-paste was horrible. Until 2012 PHP got a Composer, thanks to PHP FIGs standards. It's goal to manage package dependencies and autoload code in runtime.
Biggest difference is that in PHP dependencies are stored on project level (each project download own set) and there is no potential collisions between projects.
Code autoloading is based on Namespaces. And on practice it works better that Constant Autoload in Rails with Ruby modules. In PHP all Composer packages have unique vendor Namespace and it is very easy to track what code is being loaded and specify it directly. Really I have a lot of issues with it in Rails, but no issues at all in PHP.
PHP has The League of Extraordinary Packages - a group of developers who have banded together to build solid, well tested PHP packages using modern coding standards. They have a lot of high-quality independent packages for Routing, DI, Events, Commands, OAuth, Time manipulations and a lot more. And there are any single constraint for you to use these great libs with any PHP framework. You can even combine them and build your own kind-a framework!
Another great example Aura for PHP - that is literally a complete full-featured framework, but consists solely from loose coupled components, you can use them independently or combine as you need.
[PHP] ... fragmented, independent and isolated communities ... is one big ocean of disconnected islands.
Hm... being reusable, isolated and loosely coupled is not "fragmented or disconnected", and the Majestic Monolith is not the only option!
Monkey-patching
Should I mention that PHP has not monkey-patching by design? And packages are not colliding when they monkey patch the same method. Yes this is the limitation of PHP, you can't do a lot of nice magic as Ruby, but I would really better live without these "sharp knifes" and would better think about OOP code design, than bandaids.
Big advantage in PHP is that utility libraries are provided explicitly. Yes there are a number of them, most frameworks supply their own. But any framework push anything into your app code.
But wait
5.minutes.ago
is so amazing!
Not really. Very good motivation for this is given by realntl on Reddit (read full text)
It's generally bad form to embed magic numbers in code. Constants are preferred, and because of that, if you use Integer#minutes from ActiveSupport, you will probably end up with code like this:
class Foo
MINUTES = 5
def bar
MINUTES.minutes.ago
end
end
Notice the redundancy that shows up the moment I have assigned the actual value to a variable. This means that the benefit of Integer#minutes is only apparent when the primitive value is embedded directly in the code -- which is actually the least desirable place to put the primitive value. The other drawback of Integer#minutes (and, of course, the days/weeks counterparts) is that they are generally used when dealing with a time duration relative to another point in time -- usually "now." In those cases, you are going to want to be able specify the time source so that the behavior can be controlled properly for testing. Without the ability to control time, you'll need a timecop style gem to control time correctly. Now everything about what you're working on has grown vastly more elaborate and implicit. That's predictable: implicit code always creates problems and then invites more implicit code to band-aid those problems.
So what is the good way to do 5.minutes.ago
in Ruby?
To take a decoupled Time library and do this TimeMath.minutes.decrease(Time.now, 5)
In Rails monkey-patching is put in favor of code beauty, to hide the problem of "fat controller" Rails tries to do Controller code smaller via some funky shortcuts like Array#second_to_last
and Array#third_to_last
or ArrayInquirer. Sorry, but this is already got ridiculous.
(Ugly) Syntax
Ruby has definitely more opportunities to write "nicer" code. PHP will never be even close. Because it requires explicit $this
, has semicolons and brackets :) And of course it is only a matter time to get used to. With a good IDE it does not influence on writing speed or readability.
Ruby has ability to define cool predicate methods like admin?
/access?
, in PHP you can't use ?
in method name and predicates in PHP are likely to end up as isAdmin
/hasAccess
.
PHP has optional(!) Type Hinting. And believe me - when you are used to it, it is a great feature. When you want to do your code more strict (input/output) you can do via language-native things. It adds natural support for DI Containers implementation and nice autocomplete in IDEs.
DSL
PHP can't do DSL, at least in the way we know it in Ruby.
DSLs are good, in cases like it is used in Vagrantfile. But it is not good, when it is overused without a good purpose, DSL-first IMO is wrong. You should build a solid OOP construction first and then simplify it via DSL. But everything that is done via DSL should be possible without it. Than we achieve principle "Simple is easy, complex is possible".
Metaprogramming
Ruby has unbeatable ability for dynamic runtime composition. Developer can redefine Class entries (methods etc.). God, I hate this so much! :D Because you never know "where to find this ***king method definition". But this is a "sharp knife" and in good hands developer can do a great things.
In PHP there is no such power. The only magic you can do it to use "magic methods" like __get
to catch and respond on non-existing property or __call
to respond on non-existing method. There is a number of such methods. And the less possibilities to do a magic you have, the easier it is to trace.
Frameworks
Finally we go to the main point. PHP world has a whole competitive world of frameworks. This is exactly these guys a forming PHP FIG and accepting mutual agreements. And there is no unanimous leader. There is a great competition for more than 7 years. And people write any kind of apps with all of them.
Full featured frameworks
- Laravel 5 - one of the most trending framework for the recent 2-3 years, with a lot of DDD architecture approaches
- Symfony 3 - considered one of the most Enterprise-like frameworks, decoupled components and keeps app code isolation from the framework.
- Yii 2 - imo one of the most Rails-like frameworks, quite solid and coupled, in favour of dev speed.
- Phalcon - quite unique PHP framework as C-extensions, a magnitude bigger RPS.
- Zend 2 - one of the oldest frameworks, baked by PHP maintainers company Zend, but nevertheless has very low influence on market of PHP frames.
Most of these frameworks were initially inspired by Rails or other PHP frame (that in turn was inspired by Rails :)). And first version of Yii framework was also very resistive to changes in conventions etc. But as you see all of them are already reconsidered and rewritten number of times. And a lot of times it was a major changes in architectural thinking.
PHP got the "second" generation of frameworks at least couple years ago! They try to operate decoupled components and entities. They respect the packages ecosystem and most of them require just a tiny wrapper (or no wrapper at all) for a package to feel inside a framework "like at home".
Also there is a parallel universe of mini frameworks, some of them are stripped versions of "big brothers" (Lumen < Laravel, Silex < Symfony). But they provides solid and polished routing + DI level, to build you business logic as you wish.
What we have in Ruby world? The answer is clear - Rails. And Sinatra as mini-framework.
Keep fingers crossed for Hanami.
Architecture
Unfortunately in PHP I don't know decoupled architectural framework (as good as Trailblazer in Ruby). each framework try to propose own architectural approach. For example Laravel proposes DI, RequestForms, Events and Command/Handler, Entity/Repository etc.
Most of the frameworks in PHP encourage components separation and dependency injection, but unluckily as in Rails - no one teaches good usage of these approaches. And it is all depends on developer by itself.
ORM / DAO / Data Access
It started from ActiveRecord like ORMs, but quickly evaluated to Entity/Repository approach. Also there a lot of competitors Yii AR with DAO layer (btw it has multy-db support and cross-db Joins, even Relational DB + NoSQL), Doctrine, Eloquent, PropelORM etc.
In Rails we have ActiveRecord - and the whole world is spinning around it. Hanami has it's DataMapper approach. And there is a framework agnostic lib Sequel.
Interesting fact is that creator of DataMapper in Ruby became NoORM-person and now considers all ORM approaches are not worth of efforts.
Static Assets
Rails has great tool Sprockets that was magically cool in early days - it can compile and combine JS, CSS, SASS, LESS etc. You can relax and forget about it.
PHP frameworks initially had nothing like this. As PHP do not run own server and file watchers. And Rails was superior.
But JS/CSS world has changed. So fast, that Sprockets just can't get that high and so fast (it is slow in dev env, assests:precompile
task takes forewer on deploy and it does not support all latest JS pre-compilers). All cutting edge JS/CSS processing is now done in Node.JS tools like WebPack/Babel. It is almost an industry standard. And Rails has lost it's advantage.
Now anyone can take Node.JS + WebPack and use all modern ES6 + SASS + JS frameworks + Hot loading despite the language and framework he uses. I do it with PHP and with Rails. I do not need Rails specific skills, when I can use general skill.
Background jobs and queues
One things that PHP lack of is background jobs runner. Due it's specific PHP processes are not meant to live for a long time. There are several approaches including PHP daemons, but PHP requires some help here - process supervisor + queue backend (Redis, Beanstalkd etc.)
Ruby has much more native tools here like DelayedJob, Sidekiq etc. And that is awesome, when you have delayed operations "for free" without a lot of thinking - you can do better start at the beginning. But eventually scalable Ruby job queues end up with Redis etc.
IDE support and code completion
Initially I was wondering why a lot of Ruby devs do not use big IDE, but Sublime etc. Then I got that even such cools IDEs like RubyMine gives you almost nothing in terms of autocomplete for Ruby. Because of its dynamic nature. And that still frustrates me.
In PHP, despite it is also a dynamic script language, autocomplete is much better. And it has widespread PhpDoc standard to annotate your code to give almost 100% correct autocomplete even for dynamic Factories. Framework and libs code is allover annotated, it is kind a rules of etiquette in PHP to annotate a code. And I miss this Ruby so much.
Tests
Ruby has outstanding testing possibilities due to it's ability to reopen classes and make native stubs/mocks. It really does not require to use DI to change heavy code behavior in Tests. With RSpec, Minitest and Cucumber - you can do any kind of tests Unit, Integration and BDD style.
PHP also has a number test tools - main is PHPUnit. It provides a solid foundation for different asserts and Mocks. Codeception test framework - provides BDD style Unit and Integration tests. There are also approaches to copy Cucumber (like Behat), but I think it is barely usable in PHP. The only limitation is that in Unit test you can dirty tricks and really need to think about dependency injection or Mock entry points in your classes.
Yes TDD is not dead, you just need to architect you code.
Conclusion
And there is no conclusion.
In any case I do not encourage you to leave Ruby and start doing PHP!
I just hope this post convince someone, that Ruby on Rails is not such a big and important Elephant in the Big Room of Web Applications Development. For a long time people in "parallel PHP universe" are doing a great products and have a good time with the tools not worse, than in Ruby world.
If you can't see so wide - you know who to blame!
Update 2016.06.01
Based on comments here and Reddit I want to collect the main corrections/complaints.
That's not about Ruby on Rails, but about pluralism and agnosticism
I do not want to discuss flaws of Rails app architecture in this article. It is not the main goal. I try to show how hard does it influence on the whole ecosystem in Ruby. And how different is the situation in PHP world with wide pluralism of frameworks and agnosticism of libraries.
This post is much more about Ecosystems in general, but but not about particular frameworks or a single aspect of a language (like monkey-patching).
5.minutes.ago
Ok, just stop it :) We are always talking about different things. One side convince in "code beauty", but another side blame it for "internal state". And while we will not discuss exactly the same aspect of this 5.minutes.ago
phenomena - it will be useless.
Laravel
Curious that he complains about RoR but states Laravel as a good example, Laravel is practically the RoR of PHP.
In terms of features [RoR and Laravel share a few], but surely in terms of philosophy they share a huge amount more?
It doesn't really matter how much Laravel and Rails do share, and how authoritative /u/utotwel/ is. What is more important, and what I tried to emphasize - Laravel is not hurting the PHP ecosystem, no matter what is happening inside Laravel's ecosystem (and that is natural for PHP, as language encourage to write reusable solutions, and just to make wrappers for framework integration, not vice versa). But Rails - do hurt and influence on each aspect of Ruby ecosystem.
Laravel and DDD, yes I made too loud statement, I just want to stress that despite it's Rails inspiration it has a lot of advanced architectural features out of the box: dependency injection, request forms, command bus, etc.
Behat / Cucumber
I wonder what prompted "barely usable". I love Behat.
Because it's not a testing tool :) It's a communication tool to help you to bridge the gap and avoid misunderstandings between your business and your technical team. It can be used for integration tests as well, but does a poor job at that.
I am really surprised how much people are rising their hands in favor of Behat / Cucumber. And sorry, it seems that my words were to sharp. I have always seen Behat as a testing tool. I have very opinionated vision on automated testing, and Consider Gherkin approach not simplest way to go (disregard Ruby or PHP implementation). Testing is very sensitive things and I prefer to keep is as close to native code as possible (with easy debug and autocomplete), i.e. use PHPUnit+Codeception in PHP or Minitest+Capybara in Ruby.
Architecture
I was pointed to a PHP example of architectural framework: Broadway
Broadway is a project providing infrastructure and testing helpers for creating CQRS and event sourced applications. Broadway tries hard to not get in your way. The project contains several loosely coupled components that can be used together to provide a full CQRS\ES experience.