We're getting real with the realization that system tests have failed to be worth their weight. We're killing ALL the system tests (359 cases) in HEY, replacing them with a minimal set of smoke tests. Then leaning on controller integration tests instead. https://t.co/Dj7BFI62Os
For many years, I considered all talk about scale to be irrelevant. If you throw enough money at it, any web stack can scale to handle more traffic.
With an abundance of free investor money, I've always believed that startups should choose the stack that allows them to move faster and further.
As focus shifted to profitability in the recent year or two, cost control has become an important concern.
PHP is my favorite programming language, but it blocks execution threads and network connections (DB, HTTP, Cache, etc.). Should we abandon PHP? NO! is my answer! Why should development speed be sacrificed for cost control? Why not have it both ways?
I placed a bet on PHP and added GoLang to the stack and the results exceeded my expectations.
Here's a look at my current stack (for cost control):
1. All web clients talk to a load balancer.
2. The load balancer handles SSL & routes most requests to the PHP monolith.
3. Requests to endpoints that receive high traffic & perform many I/O operations are routed to a Go web server.
4. The PHP monolith & Go server dispatch tasks to the queue & read/write from/to database & cache clusters.
5. A separate Go program handles background jobs (from the queue) and periodic tasks (scheduled).
What do I get from Go?
- Handlers waiting for I/O operations yield to the runtime so others can run.
- I can handle concurrent workloads without having to start new worker processes. Which translates to less memory consumption.
- Network connections are kept alive & re-used.
- Database connections are put back for reuse once the query finishes. They aren't kept hostage until a response is sent.
What do I get by keeping PHP?
The tooling available in the ecosystem (& Laravel in specific) is lightyears ahead. It allows developers to get things done faster by spending their time adding functionality rather than re-inventing the wheel.
Should you switch to this stack?
Are you struggling with compute infrastructure cost at the moment? If the answer is no, stick with PHP. It'll help you move faster, add value, onboard more customers, and hopefully get crazy traffic so you start having infrastructure cost concerns :) That's a nice problem to have, specially that solutions are available.
I didn't have time to write you a short answer, so I wrote you a long one
- Often people feel the desire to have "attribute-level" validations because they don't have enough controllers. The 1:1 mapping of resource and controller is great early on but it is actively harmful if you have two different user roles doing different operations on an object through the same controller endpoint. Just because you are editing columns on the `invoices` table does not mean that you should do it through `invoices#update`. If only admins can backdate an invoice, do that through `invoices/backdate#create` and authorize there. It will be super clear and confident.
- A whole class of authorization goes away if you scope things to the current tenant / user with `Current.account.invoices.find(params[:id])`. In a SaaS project it should be a huge yellow flag when you are querying models directly. You can take this a step further too: `Current.user.backdateable_invoices.find(id)` where non-admins get `Invoice.none` returned
- I find gems like Pundit/ActivePolicy/CanCanCan etc to be a net negative: authorization code should be first and foremost extremely clear and explicit. DSLs and conventions that reduce character count obscure this and are surface area for nasty bugs.
Again if you have more targeted controllers, it doesn't matter that you have some extra code to authorize. And less gems means less chance of getting blocked on a Rails upgrade, less external updates to manage, less bikeshedding over people wanting to use their pet favorite.
- Favor UI patterns that make it hard for users to do things they don't have permission to do. If you can't backdate an invoice because of permissions, don't show the Backdate button. Or show it disabled with text explaining that you dont have permissions. Keep the backend checks, of course, but let it just blow up with a generic 500. Avoid showing every user the form and then doing inline form validation errors. If you look at 37s code, they have almost no validations. Rails inline form validations are very limited and frankly kind of suck to configure -- don't reach for a different form builder, just realize you probably shouldn't be using them that much.
- For any complex permission schemes, you will want to implement "role-based access control" (RBAC is what you want to search for). User has_many :roles, Role has_many :permissions, Permission is what you check for authorizations. Making these database tables (even if you think it's overkill) is really nice in the future since eventually accounts will want custom roles and custom configurations of permissions.
Again don't reach for gems for this. You will be glad that you have full control over the system when you need to migrate/deprecate/add strange behaviors/etc as your needs change.
- One last note: how do you authorize when changes are made via an API or a third-party integration or a webhook? You don't know what `User` is actually doing it. So if you bake authorization checks deep into the model, you'll end up creating mess as you have to make fake user records or pass flags to skip checks. Another +1 for controllers as the primary point to authorize IMO
Testing is really hard to learn. It's a lot harder than I think most people realize.
But once you've learned it and made it a habit, actually DOING it is easy.
In fact, programming with tests is a lot easier than programming without them.
One of my Laravel ✨ best practices ✨ is only using the 7 "restful" methods in my controllers.
Controllers only have: index, show, create, store, edit, update, delete.
Think you need another method? You really need another controller. Built *all* of Vapor like this.
5 Essential Programming Principles You Should Know:
1. KISS - Keep It Simple, Stupid
2. YAGNI - You Aren't Gonna Need It
3. DRY - Don't Repeat Yourself
4. DTIIW - Don't Touch If It Works
5. SOC - Separation Of Concerns
I like to have expressive, powerful methods on my models, like "$invoice->toPdf()"...
But, putting the PDF logic within the Invoice model is no good. 🤮
A common pattern I use is to let the expressive "convenience" method defer to an action that actually does the work. 💪
GAMEWEEK 1 (THREAD)
▪️ Rundown of every team
▪️ Predicted lineups for GW1
▪️ Set piece takers
▪️ Top 3 FPL options
If you found this useful, feel free to like and share❤️
The last few months I've been super busy building native iOS and Android apps for my SaaS app. This week, they got approved by Apple and Google, and my customers are now using it. 🙌
I have so much I want to share about the process, because I learned a TON.
Some highlights:
Rails version 6.0.3.4 has been released! This is a security release, so please upgrade when you can. More information can be found here: https://t.co/YGd2NoiGGM
Have a great day!!!
🦠 2020 really sucked for everyone so:
🎁 Giving away another ~$3,000 🍎 MacBook Pro 16" to a random person who retweets this tweet to cheer every1 up
👩🎨 Please make something creative with it
(you have to follow for winner DM)
Proof of last time: https://t.co/iQa23a9mwm
To celebrate hitting 90K Twitter followers, we're going to give $900 to one of YOUR FOLLOWERS!
How it works:
• Retweet this tweet
• We will pick a winner at random - you have to be following us
• Winner gets to organize a giveaway on their account which we will boost
@semicolonandson I've not published the gem yet but you can find at https://t.co/kyMl5zcUx9. Subscriptions will be much simpler in your Rails application like this