Moving my serverless project to Ruby on Rails
frantic.im
frantic.im
Regarding hosting, I think this is actually a great time to host Rails apps either via Heroku, via the new Digital Ocean k8s app cluster or – and this is my new favourite tool – using Hatchbox.io to deploy apps to Digital Ocean or AWS. It’s a dream!
It's comforting to see I'm not alone. I just wanna ship clean code and contribute value to the business, while all the young bucks in my team are more interested in rewriting our REST apis in Graphql (with all kinds of rationalizations). Looks like the younger you are the more eager you are to explore new tech and the older you are the more keen you are in just delivering value in a boring old stack.
Same story here. Our company is the only API user, why go through all that trouble? Donno.
So that weekend could rabbit hole into complexity figuring out which framework to use.
You don't have to make the whole app a SPA to use these tools. You can just use it in those few pages that really need it.
Alternative like Preact is very small and can be easily (pre)cached on the client side.
I wanna say something nice about rails too so I’ll say I have never seen a team so quickly deliver high quality web app features than a well oiled rails team. It’s something to behold.
But that being said, the primary driver for tools should be what the developers know and ease of access finding developers who know this technology. If the city you work in mostly has PHP developers, PHP is a great choice. Similar for Java, Haskell, Lisp, etc. My point is that the tool ”Rails” definitely is adequate for this problem (minus streaming video...). Look at Shopify, Github or any other massive Rails app
Yeah, if you are serving video at that rate, there are plenty of CDNs to work with, why would you submit your app engine to that.
https://stackoverflow.com/a/373188
> Wikipedia seems to be 30000 to 70000 [requests] per second spread over 300 servers
So 2k is not unusual for bigger news sites, that may or may not employ a (few) rails team(s).
Financial exchanges are doing 100,000rps of transactions per sever. That’s hard.
If we were using Java it would have taken us three times the people and four times as long to build the site, and we all would have been laid off.
That says nothing about the added cost of running inefficient services, which require additional nodes to serve the same requests and thus increase operational cost and also risk to perform the same service.
In a CRUD app the Rails layer should be extremely thin and the storage layer(s) should be doing nearly all of the heavy lifting.
There is a level of traffic at which even a "properly" thin Rails layer becomes the bottleneck, relative to many other frameworks.
TechEmpower benchmarks suggest it is around 2,500 requests per second in their "multiple queries" benchmark. In a more real-world scenario that might be 1,000 req/sec or less.
https://www.techempower.com/benchmarks/#section=data-r19&hw=...
If one is attempting to serve more requests than this per minute then yes, perhaps Rails is the bottleneck. Admittedly, Rails' large memory consumption relative to other frameworks means it can be tough (well, technically easy, but expensive) to scale horizontally at this point.
That said, there are definitely some self-inflicted wounds people run into here. Of course there are the oft-cited expert beginner problems like n+1 queries and missing indices, but there's a more subtle issue as well: ActiveRecord itself is so convenient that it discourages writing baseline efficient code even when you need it. Basically any time you need to process a medium to large amount of data, ActiveRecord acts like a sort of super boxed type (to use Java terminology) adding a huge amount of overhead. Yes it's true that ruby doesn't even have true primitive types that can achieve the performance of Go or Java, but often times ruby primitives are plenty fast, you just have to be willing to write a bit of extra code, and ActiveRecord as an ORM has plenty of escape hatches at various levels of granularity to facilitate this (eg. pluck, find_by_sql, update_all, execute, etc).
At the places who did handle this well, the "high-performance code" that needs to be hand-tuned SQL is usually much smaller than you think (a few queries here and there), and ActiveRecord is still great for your simple queries or for smallish tables.
My understanding is that MRI Ruby provides non-block IO operations if you wrap them in a thread and that it is only CPU bound tasks that are blocked by the GVL.
Is there some other issue related to that?
(JRuby provides fully multi-threading for cpu bound tasks without GVL).
All IO operations in ruby are subject to the GIL (global interpreter lock).
JRuby, for example, has no GVL including for CPU based code; everything there can run in parallel.
Even in MRI Ruby though, wrapping IO operations in a thread allows you to release the GVL when the IO operation blocks.
e.g.
5.times.map do
Thread.new do
Net::HTTP.get('example.com', '/index.html')
end
end.each(&:join)
Will perform those network requests in parallel rather than sequencially. This is how ActiveRecord can perform asynchronous database calls in parallel on MRI Ruby.I got that HTTP example from[1], which has a good write up but it's also covered in Working with Ruby Threads by Jesse Storimer[2].
I asked the original question because in the Concurrent-Ruby Readme they discuss Ruby's mutable references and the possibility of thread safety issues because of that.[3]
1. https://pawelurbanek.com/ruby-concurrent-requests
2. https://www.goodreads.com/book/show/17826435-working-with-ru...
I currently work on a rather large Rails app for a site that most of us here use. A common pattern for our performance pitfalls are things like this:
Tag.all.map(&:name)
versus Tag.all.pluck(:name)
Using `#map` will instantiate a `Tag` object and do all the slow(er) metaprogramming to get you the syntactic sugar that makes interacting with an ActiveRecord object a treat. It does this, and then you only hit `#name`, completely wasting all that effort.`#pluck` will change the SQL from `select * from tags` to `select tags.name from tags` and never instantiate a `Tag` object, instead short-circuiting and directly fetching the resulting SQL rows — which comes back as an array. It's something along the lines of:
ActiveRecord::Base.connection.exec_query(Tag.select(:id).to_sql)
Another one I see: ProgrammingLanguage.where(tag_id: @user.tags.select(&:language_tag?).map(&:id))
versus ProgrammingLanguage.where(tag_id: @user.tags.select(:id).where(type: 'LanguageTag'))
The first example loops over the loaded `@user.tags`, loads them if they're not already `#loaded?`, selects ones that are `type == 'LanguageTag'`, only to grab the `#id`.The second example joins the two resulting SQL statements and calls `#to_sql` on the second statement, building one query from two queries.
Are these times when the first example would be preferred? Yeah, plenty! If your result is already `#loaded?`, then you probably don't need to hit the database again. But for this example and the ones I'm stumbling across while getting our company up-to-speed on "good ways", these are the the commonalities.
Save for only very recently, the company I work for hasn't put emphasis on real-world Ruby/Rails skills, instead "if you can code at a high level for any language, we think you can make reasonable contributions to our codebase." This has lead to hiring skilled developers that just don't know that there's a more preferred way of doing things in Rails for different contexts.
Double-edged sword, really.
It's a good example of choosing magic/brevity over expressiveness. You don't know that Tag.all.map calls SQL because it's not something you explicitly tell it to do. That's the real issue with Ruby & Rails. The magic lets you do some powerful stuff but sometimes it's hard to tell what exactly is happening.
Confusion here simply shouldn't be a problem for even a moderately seasoned developer, and if they do make such a mistake (because hey, we all make mistakes...) in performance-sensitive code they could quickly recognize it for what it is - a bug - and fix it.
If you're hiring junior developers, on the other hand? Sure! But you should know what you're getting, and your code review / mentoring process should get them straight.
I'm not sure I really understand how this is Ruby's or Rails' fault, unless your premise is "ORMs considered harmful" - in which case, ActiveRecord is far from alone here, and that's a different sort of conversation.
I also deal with a lot of scale, the issues people have here doesn’t match my reality. I think people have issues and rather than looking at what is fundamentally happening with their call patterns, they jump to calling out rails itself.
Rails does have some specific issues, but you’d have to go pretty deep to see them and boot times are terrible.
https://medium.com/coryodaniel/from-erverless-to-elixir-4875...
Elixir/Phoenix solves a lot of these pain points and is a joy to work with. If you like Ruby/Rails then you already know quite a bit of Phoenix. Schedule yourself 4 hours to follow a tutorial or book.
Also Pragmatic Programmers has very good books on both, written by the creators.
I love Phoenix but I will disagree with the OP in 2 points.
Yes probably Elixir/Phoenix will manage better thousands of concurrent connections, but if you are doing personal projects or are a start-up that condition is irrelevant in the meantime. So that only lefts us with the big established applications. That does not mean Phoenix is not good, it only means you will not notice a difference with Rails for most of your projects.
The second thing, that OP failed to mention is that the Rails ecosystem is at least an order of magnitude larger. From the gems available, job opportunities,developers available, instructional material, SOP for many tasks and so on.That is boring, but valuable.
The official online documentation is pretty good too.
Lack of library support was a bit annoying at first. But then I realized that, back when I used to take advantage of existing libraries in Ruby/Rails, in the vast majority of situations I was just utilizing a small portion of those libraries anyway. It ended up easy enough to write my own code for those features.
It has the upsides of Rails without the connected antipatterns, native technical debt.
I can also confirm that there isn't a need to learn BEAM gotchas (which i did learn). One of my ex colleagues is a junior dev who has been using elixir for 3 years now and didn't need to use those special aspects of BEAM.
As a Ruby expert, Elixir is substantially easier, learning BEAM is a joke (and very instructive) compared to learning the entirety of the Ruby object model.
I can't think of reasons to use Rails nowdays. If you are in the CRUD apps market, use Postgrest or similar products. As for the rest sure, choose any language you want, the framework is way less relevant (arguably: detrimental) at that point.
The industry has proven extensively that for non-trivial projects, Rails is damaging due to ActiveRecord.
Whenever I take the plunge I am always reassured; Ruby and Rails does make me happy.
However, I got tired of the lack in conventions in Express, and Firebase has quite a few gotchas and limitations, so I'm moving to good old Django. I'm delighted thus far, I love not having to reinvent the wheel every time I start a project or add a feature.
I'm probably keeping React/Next.js as my primary working tools for the front-end nonetheless, but that's just me because I feel highly productive with them and I've become accustomed to the decoupled client/API architecture.
I can see the merits of SSR with templating as seen in RoR or Python, and I'm happy to have that tool in my belt. But conversely I also think it's worth for pretty much everyone to learn React (or Vue, Svelte, or what have you) because they do open different possibilities in UX engineering and app architecture, and the market is quite hot for them too.
Granted Rails isn’t the answer to everything. I’ve been loving what we can accomplish with elixir/Phoenix. But I’ll leave the hipsterism to the JS community and marvel as they reinvent wheels over and over again.
Honest question: what leads you to believe that Ruby on Rails, or even Ruby, is the right tool for the right job? You didn't even mentioned the job, so why do you automatically assume Ruby is the right tool?
Additionally, by ignoring popularity you're also ignoring availability of documentation and examples and mindshare. You're also ignoring experience, and prior onboarding into a language stack, which automatically means odds are anyone onboarding into the project will quickly be up and running.
Speaking as someone who was forced to onboard into a Ruby project just because a predecessor jumped on the bandwagon, the experience was an unmitigated disaster. A minor onboarding task that consisted of tweaking a hard codes settin in a module required me and a couple of colleagues to spend a few days a) learning a brand new programming language, b) learning exotic frameworks, c) getting acquainted with idiomatic Ruby, d) learning how to troubleshoot and debug Ruby applications, e) properly setup a software dev environment, and f) finally fix the issue.
If my predecessor opted to use Python instead of succumbing to bandwagons and resume-driven development practices, the same thing would take a couple of man/hours.
How does can this sort of snafu pass off as the right tool for the right job?
This might mean that this wasn't the right tool _for your team_, but it's not a comment on what jobs are a good fit with RoR.
Isn't that already an operational problem? I mean, consider the mental burden alone of being forced to onboard to a completely different and relatively obscure tech stack, including the quirky programming language that serves as it's base, and all just to keep a web service up and running.
That's my opinion but after maintaining some large Rails apps and large Django apps, I would still take Rails any day. Just the testing quality and the batteries included makes it worth it, whereas the main downsides of Rails (speed and lack of typing) are also there anyways on a Django project.
That aside, if people building the project don't have any experience with what they are doing, it will be messy regardless of the framework or the language.
That's why popularity matters. The odds that any random kid already has experience with Python are far greater than him knowing Ruby.
It seems like it’s great for quickly building things, but it is unmaintainable, unless you wrote the thing. I am very flexible otherwise, but Ruby on Rails is banned from any project I’m involved in.
That said, Rails projects without tests aren't fun. Besides missing tests, I've also seen fat controllers, bad schema design, and not using Rubocop make more issues with maintenance, but usually I interpret as lack of understanding how to use Rails, not Rails being to blame.
Applies with every language and framework one is not familiar with.
* Elixir is a great functional programming language
* When your backend needs to do more complex or longer running work it's just more Elixir rather than complicated architecture involving task queues etc. (because you have the concurrency of the BEAM to manage it)
* You have options like LiveView for your frontend. You don't have to use it, but it's there.
I agree with what you said about testability for Lambda style architectures. How do you spin up an offline version of some or all of that to see if it works? The BEAM world is nice for me because you can run a similar architecture of separate things (albeit all in one language), but have a much better testing setup.
Worth noting Stimulus Reflex uses the same approach as LiveView (most stacks have similar options)
I have never used Stimulus or Stimulus Reflex, but based on his output Chris is a smart guy and an excellent developer who's opinion I therefore give some weight to.
When you use Phoenix, the Web framework creates a process to handle the specific request. By default everything you do happens in that process (give or take some database stuff). If you make a programming error that process will 'crash' (again a BEAM term that is more like an exception) and the user will get an error. No other processes will be affected, regardless of whether they are to do with other requests or anything else.
If you want a work queue, you would create a separate set of BEAM processes (or maybe just one), probably at startup. In your request handling you would send a message to that work queue process asking it to do things. Your request can block (not affecting any other requests) waiting for it if necessary, or it can return so you can get the result later. If the Web request times out or crashes or whatever, that will kill the handler process, but will leave the work queue process alone.
One other note - under the hood it's all message passing between processes, but to the programmer each process is probably just a Gen Server (an abstraction) and the message send and reply is just a function call. But normal Phoenix stuff doesn't even have that because the Web server does the 'process machinery' for handling requests.
1. Some process group libraries can handle graceful shutdown, which migrates the processes to other nodes. When you kill one of the nodes, it's process goes to other nodes in like tens of seconds.
2. Invent some virtual actor pattern like Microsoft Orleans.
3. Just backup processes states to external storage. Even not for shutting down nodes scenarios, people still tend to backup process state to ETS or stateful processes so that stateless processes can crash-then-revive without troubles.
I personally quit PHP around 2013 because of the inconsistent standard library. I spent more time looking up the docs than developing because the language just wasn't intuitive compared to let's say Python. However, beyond that, PHP has some pretty great frameworks nowadays (and I am forever grateful to Laravel for teaching me the concepts of an MVC framework, which proved useful when I switched to Django).
It is not that Ruby suddenly popular again, the scaling issues is still there, Scaling not impossible but mostly have to do with being expensive. If you are an average dev in UK or US with 100K+ Salary of course scaling is cheap. But not everyone has that luxury or operate on the same budget. But the combination of Ruby and Rails getting faster and Hardware is finally getting cheaper. ( 128 Core EPYC, on a 2 Socket Server Changes the Unit Cost of Core per VM across the whole industry ) Along with wages rises across other part of the world.
And the world has finally woken up to may be Javascript Ecosystem isn't exactly what they have hoped for in the backend.
Or, you know, just do related functionality in the same AWS Lambda function. While I would probably do the sending of an e-mail asynchronously as well (as AWS Lambda function triggered by a DynamoDB stream), the indirection over SNS to write something to DynamoDB seems overly complex.
Why would you do something asynchronously with a Serverless architecture you'd do synchronously with Ruby on Rails?
> […] and random hardcoded configs in AWS console.
Just don't do that. A Serverless application will always be a pita if it relies on manual configuration. Ensure all relevant configuration is part of Infrastructure as Code (e.g. CloudFormation or Terraform).
> Impossible to replicate production environment
If the project is set up properly with Infrastructure as Code replicating the production environment is as easy as it can possibly get. And because it's Serverless, if an environment isn't used, it usually doesn't even incur notable costs.
There is no silver bullet and I believe Serverless as well as other approaches have their place, but what I miss are similar posts about great experiences with Serverless applications. I for myself wouldn't want to go back and I'm really happy with the problems Serverless applications solve.
At my job I use AWS serverless services and I get a lot of frustration not being able to test and debug code offline. Having each time to upload some code to debug it is time consuming. Also you have to rely only on logs to debug, you obviously cannot use a debugger, and thus the solution is to insert a ton of print statements in the code and remove them, which is not a problem to me (I usually do that even in code that I debug locally) but the service to read these logs (ColoudWatch) is not great at all, you don't even have all your logs in one place, it's a mess.
I think serverless is overrated, sure it maybe the right tool for a simple project, but when the complexity grows it's best to use other more classical solutions.
Also, AWS SAM CLI allows to run things locally.
AWS's Serverless Application Model explicitly supports local testing.
https://docs.aws.amazon.com/serverless-application-model/lat...
Which problems did you experienced?
It doesn't provide any additional capability that was already there. Ie, I could already run my arbitrary function from the CLI by building a simple wrapper that reads a JSON file and sends it in. Or wrap it in a simple HTTP server.
The debugging story using that tool is worse than how I would already do it pre-SAM and they don't even have documentation for every runtime they officially support:
https://docs.aws.amazon.com/serverless-application-model/lat...
It doesn't allow you to test any of the interesting bits, which is what the blog post was alluding to.
- What happens if I have an SQS Lambda with 100 concurrency and a few of the containers get into a bad state? Do the other ones keep going or does the whole thing grind to a halt?
- What happens if I have a Lambda that consumes from 50 Kinesis shards and some of the containers get into a bad state?
- What happens if my Lambda throws an exception, what happens to my SQS message or Kinesis record?
If you outsource the plumbing you can't actually test the system locally you can only test "units" of it and in a lot of cases you either have to do remote tests with print debugging to figure out what's going on or what is more common is blindly assume the plumbing works how you intend, ship it to Prod and then fix problems as they occur.
Sounds like you're mixing up how a test pyramid works.
AWS SAM supports running unit tests locally. Your remarks refer to integration tests. Integration tests are expected to be performed on environments that mimic either subsets of the production environment, or the whole production environment.
https://docs.aws.amazon.com/whitepapers/latest/serverless-ar...
You're forced to either share the aws resources with other devs or provision separate resources for each dev which is a big PITA.
You can have pretty much all the services locally if you want you can even have a step function locally
To make debugging at least a bit easier, I prefer doing structured logging (e.g. by utilizing AWS Lambda Powertools [1]) to get the ability to query the logs efficiently with CloudWatch Insights [2]. Also AWS X-Ray [3] is really valuable to understand how a Serverless application behaves.
[1]: https://github.com/awslabs/aws-lambda-powertools-python
[2]: https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/Ana...
For my purposes debugging is as simple as crafting a payload and feeding it through the handler locally. I match credentials locally with what my lambda runs as. Since it’s a monolith function it behaves basically the same way, including the usage of a debugger.
To make life a little easier any time there’s an exception it persists that ordinal payload so I can just replay it locally.
But I agree that serverless is mostly overrated for most use cases.
On the other hand, I use cloudflare worker on a bunch of my projects. Unlike aws serverless, cloudflare worker is actually useful because it can do many things that can't be done by traditional backend, such as running and intercepting requests on the edge servers that sit between your server and your visitors.
[1]: https://docs.aws.amazon.com/lambda/latest/dg/lambda-edge.htm...
There's definitely a new technique I've had to teach myself in order to build serverless systems effectively. Today I'm getting great results from the approach.
There might be no “silver bullet”, but aws serverless is like rusted bucket.
Is it? For me it's working fine.
What problems are you facing when writing CloudFormation templates and what better alternative to declare infrastructure do you suggest?
Most problems I see are people being not aware of the possible attributes of resources or doing indentation mistakes when it's a YAML template. Most of these problems can be avoided by using cfn-lint [1] to ensure the template is valid.
The AWS UI should ideally be refactored to be a frontend for an infrastructure as code service like CloudFormation.
One of the other problems with AWS+CF though is that it doesn't support every option from every service, e.g. populating secrets was a hassle when I used it.
CloudFormation lagging behind in terms of features is mainly a matter of priorities and I agree, feature parity from day 1 on would be great. However the way they do it allows them to ship features more quickly, so that's something as well.
In the past CloudFormation support was even worse as the CloudFormation team was responsible for implementing the support for new features of all services in CloudFormation. And with an ever growing list of services and features, that was something which simply didn't scale. From my understanding that changed however, so that most service teams are now responsible for CloudFormation support of their service themselves.
> One of the other problems with AWS+CF though is that it doesn't support every option from every service, e.g. populating secrets was a hassle when I used it.
That's because creating a secret in AWS Secrets Manager is not a control plane operation (which is what CloudFormation usually implements), but a data plane operation (which CloudFormation usually doesn't implement). However in this particular case it has been apparently painful enough, as creating secrets can be necessary to be able to deploy other resources (e.g. for authentication to the Aurora Data API), that AWS implemented this data plane operation in CloudFormation [2]. The same is true for creating parameters in the SSM Parameter Store [3], while it's still not possible to create objects in S3 or items in DynamoDB without custom CloudFormation resources.
[1]: https://github.com/aws/aws-cli/issues/3607
[2]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGui...
[3]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGui...
OP's post contains:
> What started as a simple function, grew into a bunch of serverless functions, SNS topics, S3 buckets, DynamoDB tables. All bound together with plenty of YAML glue, schema-less JSON objects passed around and random hardcoded configs in AWS console.
With SAM, it's still a ton of boilerplate too.
And honestly, even most simple functions are going to use SNS topics, S3 buckets and CloudWatch so you can see your logs. Probably a datastore too. Or if you want to access the Lambda over HTTP instead of an SNS trigger, then you'll need something like API gateway or a load balancer too.
It's even more fun if you decide to write to an RDS instance that's locked down by IP address and inside of a VPC while at the same time your Lambda needs external internet access. The amount of hoop jumping to get all of that to work was unreal. And it ended up costing $33 / month just to have a NAT set up to route all of the internet traffic between these services.
The infrastructure complexity was more than anything I've ever experienced in any tech stack. But I thought the goal of Serverless was to avoid infrastructure?
Then there's the whole problem of wanting to wire up your main non-serverless service wanting to trigger a Lambda using SNS topics. That's fine until you want to be able to develop the project locally, except SAM and other comparable tools don't mock out SNS. So now you're stuck either having to pay to have your dev environment running on AWS or set up something like localstack to mock out a bunch of AWS services (which isn't free either btw).
The messed up thing is, after all that, it was to be able to run about 20 lines of Python business logic. All it did was call out to a 3rd party API service and store the results into a database.
Serverless is typically hard to setup and hard to iterate upon. You want to have a pretty clear understanding of the specific use case you’re addressing beforehand so you can architect the system well, which isn’t easy for a solo developer / small team who just wants to setup a little function in the cloud.
Actually, the best use case for serverless is: - subscription to AWS specific events. It’s all built in and easy to plug into. - elastic ETL pipelines. This is a very enterprise focused use case which involves processing GBs/TBs of data with complex transformations. The variability in data throughput makes serverless an elegant solution. Also, your ETL pipeline isn’t going to change in the same way that a product focused use case of serverless will change.
I would go as far as to say the unfriendly local dev experience is almost the point. They want to lock you into having to use their platform to get anything done. They want to make this stuff as difficult to abstract as possible. They want you to use their proprietary methods for code sharing (looking at you lambda layers...). All of it is designed so that you simply can't run your logic without tying yourself inextricably to their platform. It's a spiderweb that you don't notice until you are trapped in it.
Yep, this matches my experiences with AWS too.
The stuff that you THINK might cost an arm and a leg, are actually really cheap. Billions of lambda calls with thousands running concurrently? meh. Huge SNS/SQS-queues? Meh. Terabytes of data stored in S3? A drop in the bucket.
BUT.
Too many PUT-operations on S3? $$$$$expensive$$$$$ Too much IO on an Aurora instance? Pucker up! Need some sane network configuration? Prepare to pay up the wazoo for gateways.
Yes, all that can be worked around, but it's really non-intuitive to actually see where the pain points in a project will eventually be.
I have started to mix Zappa and Django to get the best of both Django and Serverless. So far, I like it. I have not hit any considerable scale yet, so I wonder if Django is too heavy, or just right. It seems fine so far. The other plus - it’s all open source tech, so it’s easy to move away from Lambda if I need to.
It’s even better, too, because you CAN break out some more isolated functionality into stand alone lambdas when they start to get clumsy inside a typical Django proj.
The downside is dealing with AWS events. There’s a few ways to handle it. You can write it into your monolith, toggling behavior with say an environment variable. You can sometimes work around it, for example avoiding the S3 file uploaded event by having clients ping your HTTP API on upload completion. Of course, there are trade offs for all the techniques. You can easily reconfigure a lambda to retry on failure but you’d have to manually implement that client-side. You can also always just add another lambda if you’re ready.
To put it another way: you probably don’t need to write that record to a queue which gets written to DynamoDB, whose stream calls another lambda etc etc. Just do that stuff within a regular old HTTP handler until the fanciness is absolutely necessary.
For personal projects, I appreciate how serverless scales to zero. I don’t have to worry about axing projects with minimal usage.
[1] for example https://github.com/akrylysov/algnhsa or https://github.com/awslabs/aws-lambda-go-api-proxy
As I wrote in my last article [0], serverless advocates pushed too much FaaS in the last years.
FaaS is awesome, but should be the last resort, if you throw too many functions at a problem you can easily end up with a distributed monolith, which is often worse than a regular one.
What a beautiful house of cards.
The orchestration vs choreography problem.
https://theburningmonk.com/2020/08/choreography-vs-orchestra...
1. API Gateway and AppSync integrate directly with many other AWS services, no Lambdas needed, and thus no cold-starts.
2. Lambda has provisioned capacity.
3. Other cloud providers like Cloudflare offer FaaS without cold-starts.
I know little about lambda. Provisioned capacity, as in an instance running 24/7? Like a server?
But I guess it's the last resort if you absolutely have to use a Lambda without cold-start
It really makes sense if you have traffic that spikes at certain times per day (think like fantasy football). There’s going to be times when no one is using your application but when they do, there are going to be dozens or hundreds of people suddenly using it. So the first person that logs on hits the provisioned Lambda instantly and then AWS warms up the rest of the Lambdas as more traffic comes in.
[1]: https://docs.aws.amazon.com/lambda/latest/dg/invocation-scal...
Old boring stacks can still go a long way for small/medium projects.
Crystal is designed to feel like Ruby but to be "fast like C". I think in reality by adding a type system it manages to be better than Ruby.
Small but very welcoming community with a number of startups and companies running things in production. Crystal itself is doing well with the 1.0 release looming very soon.
Has Crystal stabilized?
I'm the author of openfaas and hear what you say about the fragmentation for using AWS Lambda. We recently made a rails-esq version of OpenFaaS for users who have a single small app like yourself. I would be interested in your impressions. It's all open source and is a bit more of a more modern stack. On the upside it uses containers and you can write in any language.
Why split up the serverless functionality into separate functions connected with SNS etc? You can call the same ruby code from a single serverless lambda endpoint invoked from a http call in the same way you would from a rails process.
Rails is great, but you could run Rails on a serverless architecture (AWS) too if you really wanted to, and you could create a multi service architecture of Rails components that would be equally hard to maintain and develop.
That said, I see many promising upstarts and incumbents in the space trying to make stateful Serverless easier by the day. Things can only improve and improve they will. I'm bullish.
In my experience, being at either extreme becomes counter-productive.
To me, it comes down to finding the "happy middle" for your project.
Monolith v/s Micro-services/Serverless are similar. Neither extreme is productive. There exists a happy middle depending on the situation.
There's a limit to how reduced data, and functions that work on data, and the transports and relationships between it all, can get. Let's call this residual mass "goo". Well, the goo has to go _somewhere_.
Aren't there any good serverless frameworks that simplify this complexity?
If you are a single developer there are barely any reason to use it
I also don't have to pay for what I'm not using, also important for a single developer. If I had chosen a more traditional approach of using EC2 to host a server, I would be paying quite a lot more for my project to sit idle on an EC2 instance 24/7/365 if I'm not getting enough traffic to support paying for the idle server.
And I feel like today we are back to some kind of a point of having to shoehorn FE/BE tech together unless we use some isomorphic approach which, predominantly, forces the use of JS.
And I remember DHH commenting on this regarding Java and saying something to the tune of "Why would I write Java when Ruby is so much better?". Back then I thought such a statement was too indignant, like someone getting on their high horse about a particular kind of building material that is better above all else. But nowadays, I see the wisdom behind it all the same.
I just want simple bloody tech that treats the browser as a layout and input handling engine and lets me write code in any language I see fit, whether it's Ruby, F#, C#, Clojure, whatever. I'm hoping Blazor or something like it can deliver on that vision.
I'm keeping a close eye on it. I also need to test how it scales with regards to handling traffic.