[HN Gopher] In defense of simple architectures
       ___________________________________________________________________
        
       In defense of simple architectures
        
       Author : abzug
       Score  : 209 points
       Date   : 2022-04-06 19:04 UTC (3 hours ago)
        
 (HTM) web link (danluu.com)
 (TXT) w3m dump (danluu.com)
        
       | lifefeed wrote:
       | I was interviewing for software jobs recently, and while I was
       | studying up on the "system design" portion I kept circling around
       | the same insight that Dan Luu writes about so well here.
       | 
       | I would sit down at an interview and try to create these "proper"
       | system designs with boxes and arrows and failovers and caches and
       | well tuned databases. But in the back of my mind I kept thinking,
       | "didn't Facebook scale to a billion users with PHP, MySQL, and
       | Memcache?"
       | 
       | It reminds me of "Command-line Tools can be 235x Faster than your
       | Hadoop Cluster" at https://adamdrake.com/command-line-tools-can-
       | be-235x-faster-... , and the occasional post by
       | https://rachelbythebay.com/w/ where she builds a box that's just
       | fast and with very basic tooling (and a lot of know-how).
        
       | aadvark69 wrote:
       | Simple architectures work well, until they don't. A good example
       | is ye olde ruby on rails monolith. Dead simple to set up and
       | iterate quickly, but once you reach a certain organization and/or
       | codebase size, velocity starts to degrade exponentially
        
       | aidos wrote:
       | In terms of the choices they're unsure about; I'd say it's best
       | to stay away from Celery / RabbitMQ if you don't really need it.
       | For us just using RQ (Redis backed queue) has been a lot less
       | hassle. Obviously it's all going to depend on your scale, but
       | it's a lot simpler.
       | 
       | RE the sqlalchemy concern; you do need to decide on where your
       | transactions are going to be managed from and have a strict rule
       | about not allow functions to commit / rollback themselves.
       | Personally I think that sqla is a great tool, it saves a lot of
       | boilerplate code (and data modelling and migrations are a
       | breeze).
       | 
       | But overall the sentiments in this article resonate with my
       | experience.
        
       | bob1029 wrote:
       | I think the biggest problem for most developers is not
       | understanding what one computer can actually do and how reliable
       | they are in practice.
       | 
       | Additionally, understanding of how tolerant 99% of businesses are
       | to real-world problems that could hypothetically arise can help
       | one not frustrate over insane edge case circumstances. I suspect
       | a non-zero number of us have spent time thinking about how we
       | could provide deterministic guarantees of uptime that even
       | unstoppable cosmic radiation or regional nuclear war couldnt
       | interrupt.
       | 
       | I genuinely hope that the recent reliability issues with cloud &
       | SAAS providers has really driven home the point that a little bit
       | of downtime is almost never a fatal issue for a business.
       | 
       | "Failover requires manual intervention" is a _feature_ , not a
       | caveat.
        
         | nomemory wrote:
         | Some people don't even realise how much traffic a simple web
         | app with server side rendering (decently written), hosted on an
         | average dedicated server can hold... They dont need cloud,
         | autoscaling, microservices, kafka, event driven architectures,
         | etc.
         | 
         | We've lost our way in the masked marketing the cloud providers
         | are creating to help us solve problems we will never encounter,
         | unless we are building the next Netflix or Facebook.
        
           | bob1029 wrote:
           | If you want to get an idea of where things are at right now,
           | this is a good place to start looking:
           | 
           | https://www.techempower.com/benchmarks
           | 
           | If you just need plaintext services, something like ~7
           | million requests per second is feasible at the moment.
           | 
           | By being clever with threading primitives, you can preserve
           | that HTTP framework performance down through your business
           | logic and persistence layers too.
        
         | cameronh90 wrote:
         | These requirements don't come out of nowhere. Normally they
         | come from:
         | 
         | 1. CEOs/whoever that don't listen to how much additional
         | complexity it is to build a system with extremely high uptime
         | and demand it anyway.
         | 
         | 2. Developers with past experience that systems going down
         | means they get called in the middle of the night.
         | 
         | 3. Industry expectations. Even if you're a small finance
         | company where all your clients are 9-5 and you could go down
         | for hours without any adverse impacts, regulators will still
         | want to see your triple redundant, automated monitoring, high
         | uptime, geographically distributed, tested fault tolerant
         | systems. Clients will want to see it. Investors will check for
         | it when they do due diligence.
         | 
         | Look at how developers build things for their own personal
         | projects and you'll see that quite often they're just held
         | together with duct tape running on a single DO instance. The
         | difference is, if something goes wrong, nobody is going to be
         | breathing down their neck about it and nobody is getting fired.
        
         | _jal wrote:
         | Past the proof of concept, "developers" should frankly not be
         | making these decisions. People who understand systems and
         | failure analysis should be. You might have devs with that
         | experience, but they're comparatively rare.
         | 
         | As far as complexity... if you get big enough, you can't avoid
         | it. My meta-rule is to only accept additional complexity if
         | solving the issue some other way is impractical.
         | 
         | It is almost always far, far easier to add additional moving
         | parts to your production environment than it is to remove them
         | after they're in use.
        
         | trasz wrote:
         | Also, those complicated architectures are often quite
         | unreliable anyway - just in ways that don't show in metrics.
         | Slack comes to mind: not only its functionality is poor
         | compared to eg IRC, but it fails in hilarious ways, eg showing
         | duplicated messages, or not showing them at all. Another
         | example is YouTube - the iOS app gets confused when displaying
         | an ad, which results in starting the playback at a wrong time
         | offset. I guess it's because companies like those don't care
         | about actual reliability - what they do care about is
         | availability.
        
           | joshlemer wrote:
           | How could you say that Slack has poor functionality compared
           | to IRC?
        
             | exfascist wrote:
             | When you type something into IRC that message shows up in
             | the log and every online users client pretty reliably.
             | Furthermore the high degree of diversity among clients
             | provides a pretty extreme amount of client side
             | functionality that Slack _completely_ lacks (scripting is a
             | huge one.)
        
               | spicybright wrote:
               | I love irc, but is is just silly.
               | 
               | Slack has much better history because you don't need to
               | have been online when messages are sent to log them.
               | Slack is absolutely more reliable in this regard.
               | 
               | IRC is easy to script because the protocol is so simple.
               | But you leave so much on the table for that cost.
               | 
               | Obviously if your use case is text only that you don't
               | care about being persistent and you lean heavily on
               | scripting to get things done then IRC will do the trick.
               | Otherwise it's such a crutch to do anything besides
               | beyond that.
        
               | exfascist wrote:
               | IRC has logs for history, they're fast and you can run
               | your own logger to control the retention policy if you
               | want. These heavy weight IM tools have extremely short
               | log retention (months) and searching through the logs is
               | _extremely_ slow and frustrating IME.
        
               | zie wrote:
               | Slack is not instantly "better" than IRC, it's just a
               | different approach to the chat problem and it's arguably
               | more approachable for people that don't want to learn
               | about the chat space.
               | 
               | Logging is just different between the two.
               | 
               | For IRC, logging is outside the scope of the IRC
               | protocol. Anyone can log anything anytime anywhere with
               | whatever policies and procedures they want. This usually
               | leads to each channel/project having some "official" log
               | of the channel somewhere, using whatever they feel is
               | good for them.
               | 
               | Slack on the other hand centralizes the logs, which
               | removes lots of control into the administrators/slack
               | developers.
               | 
               | So Slack's logs are likely easier to find, but that
               | doesn't necessarily make them easier to use.
               | 
               | Persistency is also just different, IRC makes it your
               | problem, but it's a solved problem if you care about it.
               | irccloud.com and sr.ht both offer persistence in
               | different ways as two differing examples to the problem.
               | 
               | Slack of course centralizes the problem and removes some
               | control.
               | 
               | I personally think Slack and approaches like it (I prefer
               | MatterMost) are great for internal things where
               | administrators need central control of stuff for various
               | reasons. For public things, I think Slack is a bad
               | solution, and something like IRC or Matrix is a better
               | solution to the problem of public chat.
        
               | ryukafalz wrote:
               | The versatility of clients is indeed a huge benefit of
               | IRC. I used to use IRC at work and always had my Weechat
               | window split with a small pane up top showing either my
               | highlights or a channel I needed to monitor at the time.
               | With Slack, you can't do that, which means you have to
               | repeatedly click between channels if you need to pay
               | attention to multiple at a time.
        
               | diroussel wrote:
               | You can use split view to keep an eye on another channel.
               | But another window would be better.
               | 
               | https://slack.com/intl/en-
               | gb/help/articles/4403608802963-Ope...
        
           | snvzz wrote:
           | Slack comically uses gigabytes of RAM and plenty of CPU time
           | in the client side.
        
             | musicale wrote:
             | It's a nice demonstration of the efficiency of web apps vs.
             | native apps.
        
               | sitkack wrote:
               | It really has nothing to do with that. The slack client
               | is just written poorly.
        
             | ChrisMarshallNY wrote:
             | Obligatory I Am Developer toon: https://twitter.com/iamdevl
             | oper/status/1072503943790497798/p...
        
               | yakshaving_jgt wrote:
               | I wonder who he stole that joke from.
        
               | ChrisMarshallNY wrote:
               | I would assume bruised_blood, but I can't [easily] find
               | the original, so I posted that.
        
               | yakshaving_jgt wrote:
               | No, sure. That's fair enough.
               | 
               | My point simply being that iamdevloper is a notorious
               | joke thief and is especially unsporting about it when
               | it's pointed out.
        
             | WJW wrote:
             | Wtf are you doing with it? My slack instance (on linux) is
             | resting around 300 MB resident set size and 0% cpu. 300 MB
             | is still a lot for a chat app, but it is definitely not
             | gigabytes.
        
               | guelo wrote:
               | Make sure you're counting all the sub-processes it spawns
               | (at least on Mac, don't know about linux)
        
               | dan-robertson wrote:
               | If you just add up memory usage for subprocesses you are
               | likely to over count due to shared memory. The number you
               | typically want to add up in Linux is 'proportional set
               | size' which is, I think, the sum over every page of the
               | process's memory of page_size / number of processes which
               | can access the page. I don't know what happens if you
               | mmap some physical memory twice (I think some newish Java
               | GC does this).
        
         | [deleted]
        
         | lkxijlewlf wrote:
         | > I think the biggest problem for most developers is...
         | 
         | ... reading blogs and such where some loud mouth is telling
         | them about so called "best practices" and so they bring that
         | back to work with them.
         | 
         | There are not enough loud mouths telling people to keep it
         | simple (until you can't or know better).
        
       | dang wrote:
       | What's the year on this? anybody know?
       | 
       | Normally I check the Internet Archive, but
       | https://web.archive.org/web/*/https://danluu.com/simple-arch....
        
         | Beltalowda wrote:
         | Based on Dan's Twitter, March 2022:
         | https://twitter.com/danluu/status/1501644166983421953
         | 
         | That links to the original on wave.com, dated March 9th this
         | year.
        
         | Jtsummers wrote:
         | The "previous" article at the bottom is the most recent article
         | in his archive, which was apparently published in March 2022.
         | So I'm guessing this year, and either this month or last month.
         | But the archive doesn't seem to have been updated yet with this
         | article.
        
           | dang wrote:
           | Ah ok - it's new then. Thanks to both of you!
        
         | [deleted]
        
       | gherkinnn wrote:
       | At the risk of making an ad-hominem attack, I found this website
       | unreadable.
       | 
       | Minimalism is fine. But there comes a point when there's so
       | little, it is nothing. danluu.com is a bucket of sand facing an
       | overbuilt cathedral.
        
         | chubot wrote:
         | Reader mode in your browser goes a long way, I think they all
         | have it now
        
         | dan-robertson wrote:
         | You can read the same content here:
         | https://www.wave.com/en/blog/simple-architecture/
        
         | Beltalowda wrote:
         | I set a user style in Stylus for danluu.com:
         | body { font: 16px/1.6em sans-serif; max-width: 50em; margin:
         | auto; }
         | 
         | Can even add it manually in the inspector if you want.
        
       | sydthrowaway wrote:
       | What is Wave?
        
       | taeric wrote:
       | My favorite trap in all of this, is that this thinking will fail
       | most tech interviews. It is incredibly frustrating.
        
         | winrid wrote:
         | Yep. Failed an interview because I used EJS (SSR) and Node to
         | build a simple Twitter in 30mins. The interviewer saw that it
         | was three files and did not seem impressed.
         | 
         | I guess they wanted me to use lots of little components in an
         | SPA which I did in my day job, but it didn't seem nessisary for
         | the task...
        
           | syngrog66 wrote:
           | 3 files? "Luxury!"
           | 
           | I could implement a Twitter in 1 Python or Go file, hosted on
           | 1 machine
           | 
           | granted its concurrent user capacity and traffic load
           | capacity would be insufficient for actual Twitter. but all
           | the basics would work, in the small
        
         | mkl95 wrote:
         | I guess the only thing you can do is avoid those places. Last
         | time I checked Wave were on a hiring spree.
        
         | wanda wrote:
         | I think that probably says more about the tech companies than
         | anything else.
        
         | bob1029 wrote:
         | Trap or integrated win-win?
         | 
         | We use one of these "aggressively simple" architectures too. At
         | this point, I would quit my job instantaneously if I had to
         | even look at k8s or whatever the cool kids are using these
         | days.
        
           | adra wrote:
           | Man, kubernetes is so much easier than the smattering of crap
           | that you have to jungle together before it. Puppet and co? No
           | thanks. Terraform? It's fine, but only a part of a CI/CD
           | picture. If you think the alternatives are better, I really
           | have to wonder how much of the trenches crap that people in
           | your org deal with regularly that you're insulated from.
           | That, or you're a release-quarterly kinda company?
        
             | throw0101a wrote:
             | > _Puppet and co? No thanks._
             | 
             | Puppet? _Luxury_. I started my configuration management
             | journey with cfengine. And the folks that I first heard CM
             | about started with Makefiles:
             | 
             | * http://www.infrastructures.org/papers/bootstrap/bootstrap
             | .ht...
             | 
             | * https://www.usenix.org/legacy/publications/library/procee
             | din...
        
             | hajhatten wrote:
             | We're using Nomad + Consul + a custom little cli and I
             | would never go back to K8s from this.
             | 
             | Not a yaml document in sight.
        
               | DenseComet wrote:
               | Nomad is pretty great for a lot of things, especially
               | self hosted. The only reason I prefer k8s is the
               | ecosystem. Even though there are standardized specs like
               | CSI, they were written with k8s in mind, so some drivers
               | are completely broken on Nomad. Also, most cloud
               | providers offer managed k8s, but very few offer managed
               | Nomad.
        
             | bob1029 wrote:
             | We wrote our own tools for most things. Our build is a
             | single dotnet publish command, followed by copying the
             | output to an S3 bucket for final consumption.
             | 
             | That output is 100% of what you need to run our entire
             | product stack on a blank vm.
             | 
             | Monolithic pays for itself in so many ways. Sqlite and
             | other in-process database solutions are a major factor in
             | our strategy.
        
           | WrtCdEvrydy wrote:
           | > look at k8s or whatever the cool kids are using these days.
           | 
           | I'm fine with complex architecture and would actually welcome
           | someone choosing something complex but the issue is that we
           | have perverse incentives at work to introduce stuff just to
           | pad our resume.
           | 
           | Kubernetes was designed for companies deploying thousands of
           | small APIs/applications where management is a burden. I've
           | seen companies that deploy 3 APIs running Kubernetes and
           | having issues...
        
       | reggieband wrote:
       | I understand his point but I actually think micro-services can be
       | simpler than monoliths.
       | 
       | Even for his architecture, it sounds like they have an API
       | service, a queue and some worker processes. And they already have
       | kubernetes which means they must be wrapping all of that in
       | docker. It seems like a no-brainer to me to at least separate out
       | the code for the API service from the workers so that they can
       | scale independently. And depending on the kind of work the
       | workers are doing you might separate those out into a few
       | separate code bases. Or not, I've had success on multiple
       | projects where all jobs are handled by a set of workers that have
       | a massive `switch` statement on a `jobType` field.
       | 
       | I think there is some middle ground between micro-services and
       | monoliths where the vast majority of us live. And in our minds
       | we're creating these straw-man arguments against architectures
       | that rarely exist. Like a literal single app running on a single
       | machine vs. a hundred independent micro-services stitched
       | together with ad-hoc protocols. Micro-services vs. monoliths is
       | actually a gradient where we rarely exist at either ludicrous
       | extreme.
        
       | calpaterson wrote:
       | > GraphQL libraries weren't great when we adopted GraphQL (the
       | base Python library was a port of the Javascript one so not
       | Pythonic, Graphene required a lot of boilerplate, Apollo-Android
       | produced very poorly optimized code)
       | 
       | What do people use instead of Graphene? Strawberry?
        
         | fernandogrd wrote:
         | There is also ariadne
        
       | scrubs wrote:
       | Nah, I don't much like the tone of this article. Not at all.
       | 
       | The engineering message should be: keep your architecture as
       | simple as possible. And here are some ways (to follow) on how to
       | find that minimal and complete size 2 outfit foundation in your
       | size 10 hoarder-track-suite-eye-sore.
       | 
       | Do we really need to be preached at with a warmed over redo of
       | `X' cut it for me as a kid so I really don't know why all the
       | kids think their new fangled Y is better? No we don't.
       | 
       | If you have stateless share nothing events your architecture
       | should be simple. Should or could you have stateless share
       | nothing even if that's not what you have today? That's where we
       | need to be weighing in.
       | 
       | Summary: less old guy whining/showing-off and more education.
       | Thanks. From the Breakfast club kids.
        
       | endisneigh wrote:
       | How far can you get with a single Postgres instance on a single
       | machine? I know things like cockroach and citus existence but
       | generally Postgres isn't sharded as far as I know.
        
         | zozbot234 wrote:
         | Postgres supports sharding out of the box. The documentation
         | tells you how to do it, using foreign data wrapper and table
         | partitioning.
        
         | dan-robertson wrote:
         | You can scale up that one machine a lot. If you start with a
         | normal sized machine you have a lot of overhead in increasing
         | ram/cpu on that machine (eg you could start with say 16 cores
         | and 100G ram or less and scale up to like 2TB ram and 64/128
         | cores). There's also runway for scaling things by eg shooting
         | down certain long-running queries that cause performance
         | problems or setting up read replicas.
         | 
         | So even if you're a bit worried about scaling it, you can at
         | least feel the problems are far away enough that you shouldn't
         | care until later.
        
         | zie wrote:
         | Pretty far!
        
           | endisneigh wrote:
           | How far was exactly? Like tps for reads and writes with what
           | specs?
           | 
           | I've been looking for real world performance.
        
             | zie wrote:
             | That's complicated based on workload, etc. A single PG node
             | will obviously never scale to Google or Facebook levels.
             | 
             | Attend a PG conference and you will run into plenty of
             | people running PG with similar use cases(and maybe similar
             | loads) to you.
             | 
             | I can say we run a few hundred concurrent users backed by
             | PG on a small to medium sized VPS without issues. Our DB is
             | in the 3 digit GB range on disk, but not yet TB range.
        
             | bpicolo wrote:
             | Hundreds of thousands of reads per second was pretty doable
             | even back in 2014-2015 era.
             | 
             | You can get a 60TB NVMe instance with 96 cores these days -
             | https://aws.amazon.com/ec2/instance-types/i3en/. Relational
             | databases just scream on the dang things.
        
       | AceJohnny2 wrote:
       | > _one major African market requires we operate our "primary
       | datacenter" in the country_
       | 
       | What country could that be? That sounds challenging.
        
       | surfer7837 wrote:
       | Just boils down to not optimising until you need to. Start with a
       | 3 tier web app (unless your requirements lead you to another
       | solution), then start with read replicas, load balancing,
       | sharding, redis/RabbitMQ etc
        
         | zrail wrote:
         | Realistically almost every web app can start as a one-tier web
         | app that uses SQLite as a data store and serves mostly HTML.
        
           | [deleted]
        
           | a9h74j wrote:
           | I have a dumb question ...
           | 
           | In almost all performance areas -- gaming, PCs, autos, etc --
           | there are usually _whole publications_ dedicated to
           | performing benchmarks and publishing those results.
           | 
           | Are there any publications or sites which implement a few
           | basic applications against various new-this-season "full
           | stacks" or whatnot, and document performance numbers and
           | limit-thresholds on different hardware?
           | 
           | Likewise, there must be stress-test frameworks out there. Are
           | there stress-test and scalability-test third-party services?
        
             | zie wrote:
             | Fossil SCM is a great example of a sqlite application that
             | has stood the test of time. I don't know what sqlite.org's
             | traffic is like, but it's not tiny and it runs on a tiny
             | VPS without issue(and has for years now).
        
             | SpikeMeister wrote:
             | TechEmpower has benchmarks for different web stacks:
             | https://www.techempower.com/benchmarks/
        
       | ryanbrunner wrote:
       | I think especially for small teams starting out, complex
       | architecture can be a huge trap.
       | 
       | Our architecture is extremely simple and boring - it would
       | probably be more-or-less recognizable to someone from 2010 - a
       | single Rails MVC app, 95+% server-rendered HTML, really only a
       | smattering of Javascript (some past devs did some stuff with
       | Redshift for certain data that was a bad call - we're in the
       | process of ripping that out and going back to good old Postgres)
       | 
       | Our users seem to like it though, and talk about how easy it is
       | to get set up. Looking at the site, the interactions aren't all
       | that different from what we would build if we were using a SPA.
       | But we're just 2 developers at the moment, and we can move faster
       | than much larger teams just because there's less stuff to contend
       | with.
        
         | woah wrote:
         | That doesn't sound like it's really any simpler than a json API
         | server (written in node, python, go, or anything else), and a
         | SPA. Maybe the lesson is "build with what you know if you want
         | to go fast".
        
           | ryanbrunner wrote:
           | In my experience SPAs bring a lot of headaches that you just
           | don't really need to think about with traditional HTML.
           | Browser navigation, form handling, a lot of accessibility
           | stuff comes out of the box for free, and there's one source
           | of truth about what makes a particular object valid or how
           | business logic works (which is solvable in the SPA world but
           | brings a lot of complexity when you need to share logic
           | between the client and the server, especially when they're in
           | different languages).
           | 
           | Frankly out of all the things that make our architecture
           | simple and efficient, I would say server rendered HTML is by
           | far the biggest one.
        
             | woah wrote:
             | Probably depends on the requirements. If the product should
             | basically feel like a static web page, and you are OK
             | making design and product decisions that work easily in
             | that paradigm, then a server side framework built to make
             | static web pages is going to be simpler.
             | 
             | If you have product or design requirements that it should
             | feel more dynamic like a native app, then trying to patch
             | that on top of a static webpage might get messy.
        
               | treis wrote:
               | IMHO the important thing is where your data is. If can
               | all be client side then write a SPA. If it's on the
               | server then the more you do on the server the better.
               | 
               | Returning HTML and doing a simple element replace with
               | the new content is 99.9% indistinguishable from a SPA.
        
           | ggpsv wrote:
           | There is some overhead in using SPAs when your application
           | could have been built in the way that the parent comment
           | suggests.
           | 
           | Some front-end frameworks are closing this gap but I wouldn't
           | necessary say they're equally a simple. See
           | https://macwright.com/2020/05/10/spa-fatigue.html
           | 
           | In other words, choose the right tool for the job.
        
       | pavlov wrote:
       | There are some web apps still in production that I wrote almost a
       | decade ago in Node+Express in the simplest, dumbest style
       | imaginable. The only dependencies are Express and some third-
       | party API connectors. The database is an append-only file of JSON
       | objects separated by newlines. When the app restarts, it reads
       | the file and rebuilds its memory image. All data is in RAM.
       | 
       | I figured these toys would be replaced pretty quickly, but turns
       | out they do the job for these small businesses and need very
       | little maintenance. Moving the app to a new server instance is
       | dead simple because there's basically just the script and the
       | data file to copy over, so you can do OS updates and RAM
       | increases that way. Nobody cares about a few minutes of downtime
       | once a year when that happens.
       | 
       | There are good reasons why we have containers and orchestration
       | and stuff, but it's interesting to see how well this dumb single-
       | process style works for apps that are genuinely simple.
        
         | dmw_ng wrote:
         | > database is an append-only file of JSON objects separated by
         | newlines. When the app restarts, it reads the file and rebuilds
         | its memory image. All data is in RAM
         | 
         | Apps like this tend to perform like an absolute whippet too (or
         | if they dont, getting them to perform well is often a 5 line
         | change). It's really freeing to be able to write scans and
         | filters with simple loops that still return results faster than
         | a network roundtrip to a database.
         | 
         | The problem is always growth, either GC jank from a massive
         | heap, running out of RAM, or those loops eventually catching up
         | with you. Fixing any one of these eventually involves either
         | serialization or IO, at which point the balance is destroyed
         | and a real database wins again.
        
           | Beltalowda wrote:
           | Another issue with "just a JSON file" as a database is that
           | you need to be a bit careful to avoid race conditions and the
           | like, e.g. if two web pages try to write the same database at
           | the same time. It's not an issue for all applications, and
           | not _that_ hard to get right, but does require some effort.
           | This is a huge reason I prefer SQLite for simple file storage
           | needs.
        
             | endorphine wrote:
             | Doesn't the fact that its opened in append only mode
             | (Linux) mitigate data races with regards to writes?
        
               | Beltalowda wrote:
               | Your write will be fine; that is, it's not as if data
               | from one write will be interspersed with the data from
               | another write. It's just that the order might be wrong,
               | or opening the file multiple times (possibly from
               | multiple processes) could be fun too. The program or
               | computer crashing mid-write can also cause problems.
               | Things like that.
               | 
               | Again, may not be an issue at all for loads of
               | applications. But I used a lot of "flat file databases"
               | in the past, and found it's not an issue right up to the
               | point that it is. Overall, I found SQLite simple, fast,
               | and ubiquitous enough to serve as a good fopen()
               | replacement. In some cases it can even be faster!
        
               | dmoy wrote:
               | Here is my list of numbers: 1,Here is my list of letters:
               | a,b,2,3,d
        
           | pavlov wrote:
           | Yes, you need to be sure that you understand the growth
           | pattern if you want to YOLO in RAM. If your product aims to
           | be the next Instagram, this is clearly not the architecture.
           | 
           | But a lot of small businesses are genuinely small. They may
           | not sign up new customers that often. When they do, the
           | impact to the service is often very predictable ("Amy at
           | customer X uses this every other day, she's very happy, it
           | generates 100 requests / week"). If growth picks up, there
           | would be signs well in advance of the toy service becoming an
           | actual problem.
        
             | jkaptur wrote:
             | > If your product aims to be the next Instagram, this is
             | clearly not the architecture.
             | 
             | But maybe! https://instagram-engineering.com/dismissing-
             | python-garbage-...
        
           | bob1029 wrote:
           | > The problem is always growth, either GC jank from a massive
           | heap, running out of RAM, or those loops eventually catching
           | up with you
           | 
           | Absolutely. The challenge is having enough faith that it will
           | take long enough to catch up to you.
           | 
           | Statistically speaking, it won't catch up to you and if it
           | does, it will take so long you should have seen it coming
           | from miles away and had time to prepare.
           | 
           | In my systems that use an in-memory/append-only technique, I
           | try to keep only the pointers and basic indexes in memory.
           | With modern PCIe flash storage, there is no good
           | justification for keeping big fat blobs around in memory
           | anymore.
        
         | epolanski wrote:
         | I often rewrite in my free time what I do at work without
         | dependencies and I'm often amazed at how far and faster you can
         | move.
        
         | mftb wrote:
         | I do almost this exact thing for all my personal stuff. I have
         | 5 or 6 going in a vm for simple things like my bookmarks,
         | etc... works great. I could definitely see it solving many
         | small business use-cases.
        
         | ammanley wrote:
         | Reminds me a lot of this (first paragraph):
         | https://litestream.io/blog/why-i-built-litestream/
         | 
         | Well done on building an easy-to-maintain single node app with
         | few dependencies. You would be the SWE I would send prayers of
         | thanks too after onboarding (and for not making me crawl
         | through a massive Helm chart/CloudFormation template hell).
        
         | danenania wrote:
         | Built-in first-class concurrency (ala node, golang, rust, etc.)
         | is a huge win for simple architectures, since it lets you avoid
         | adding a background queue, or at least delay it for a very long
         | time.
         | 
         | I think people are also too quick to add secondary data stores
         | and caches. If you can do everything with a transactional SQL
         | database + app process memory instead, that is generally going
         | to save you tons of trouble on ops, consistency, and versioning
         | issues, and it can perform about as well with the right table
         | design and indexes.
         | 
         | For example: instead of memcache/redis, set aside ~100 MB of
         | memory in your app process for an LRU cache. When an object is
         | requested, hit the DB with an indexed query for just the
         | 'updatedAt' timestamp (should be a sub-10ms query). If it
         | hasn't been modified, return the cached object from memory,
         | otherwise fetch the full object from the DB and update the
         | local cache. For bonus points, send an internal invalidation
         | request to any other app instances you have running when an
         | object gets updated. Now you have a fast, scalable, consistent,
         | distributed cache with minimal ops complexity. It's also quite
         | economical, since the RAM it uses is likely already over-
         | provisioned.
         | 
         | This is exactly the approach that EnvKey v2[1] is using, and
         | it's a huge breath of fresh air compared to our previous
         | architecture. Just MySQL, Node/TypeScript, and eventually
         | consistent replication to S3 for failover. We also moved to
         | Fargate from EKS (AWS kubernetes product), and that's been a
         | lot simpler to manage as well.
         | 
         | 1 - https://v2.envkey.com
        
           | gregmac wrote:
           | > For example: instead of memcache/redis, set aside a ~100 MB
           | of memory in your app process for an LRU cache. When an
           | object is requested, hit the DB with an indexed query for
           | just the 'updatedAt' timestamp (should be a sub-10ms query).
           | If it hasn't been modified, return the cached object from
           | memory, otherwise fetch the full object from the DB and
           | update the local cache.
           | 
           | I've never built something with this type of mechanism for a
           | DB query, but it's interesting. I don't think I've ever timed
           | a query like this, but I feel like it's going to be an "it
           | depends" situation based on what fields you're pulling back,
           | if you're using a covering index, just how expensive the
           | index seek operation is, and how frequently data changes.
           | I've mainly always treated it as "avoid round trips to the
           | database" -- zero queries is better than one, and one is
           | better than five.
           | 
           | I also guess it depends on how frequently it's updated: if
           | 100% of the time the timestamp is changed, you might as well
           | just fetch (no caching). Based on all the other variables
           | above, the inflection point where it makes sense to do this
           | is going to change.
           | 
           | Interesting idea though, thanks.
           | 
           | > For bonus points, send an internal invalidation request to
           | any other app instances you have running when an object gets
           | updated. Now you have a fast, scalable, consistent,
           | distributed cache with minimal ops complexity.
           | 
           | Now you have to track what other app servers exist, handle
           | failures/timeouts/etc in the invalidation call, as well as
           | have your app's logic able to work properly if this
           | invalidation doesn't happen for any reason (classic cache
           | invalidation problem). My inclination is at this point you're
           | on the path of replicating a proper cache service anyways,
           | and using Redis/Memcache/whatever would ultimately be
           | simpler.
        
             | danenania wrote:
             | It definitely does depend on various factors, but if your
             | query is indexed, both the SQL DB request and the
             | Redis/Memcache lookup of the full object are likely to be
             | dominated by internal network latency. If your object is
             | large, the DB single-field lookup could easily be faster
             | since you're sending less back over the wire.
             | 
             | In other words, a single-field indexed DB lookup can be
             | treated more like a cache request. Though for heavier/un-
             | indexed queries, your "avoid round trips to the database"
             | advice certainly applies.
             | 
             | With this architecture, the internal invalidation request
             | is just an optimization. It isn't necessary and it doesn't
             | matter if it fails, since you always check the timestamp
             | with a strongly consistent DB read before returning a
             | cached object.
        
         | smm11 wrote:
         | This. Not that I'm all about janky, but my road is littered
         | with stuff I didn't think would make it through summer, and
         | everything I check is still ticking 5, 7, 10 years later.
         | 
         | LONG ago I was amused by a Sun box in a closet that nobody knew
         | anything about. I heard about the serial label printer that
         | stopped working eight months ago, which was eight months after
         | I shut off the Sun. I brought it back up again late one Friday,
         | and the old/broken label printer magically worked again.
         | 
         | Now my stuff is that.
        
           | uxamanda wrote:
           | Ha, now you can use the label printer to label the machine!
        
         | uuyi wrote:
         | I would love to see more stuff like that.
         | 
         | An application I have written recently for personal use is a
         | double entry accounting system after GNUcash hosed itself and
         | gave me a headache. This is based on Go and SQLite. The entire
         | thing is one file (go embed rocks) and serves a simple http
         | interface with a few JS functions like it is 2002 again. The
         | back end is a proper relational model that is stored in one .db
         | file. It is fully transactional with integrity checks. To run
         | it you just start program and open a browser. To backup you
         | just copy the .db file. You can run reports straight out of
         | SQLite in a terminal if you want.
         | 
         | This whole concept could scale to tens of users fine for LOB
         | applications and consume little memory or resources.
        
           | powersurge360 wrote:
           | Check out alpinejs or stimulusjs and combine it with htmx to
           | get to a SPA like experience with very little additional
           | complexity! Htmx let's you serve partials over the wire
           | instead of a page load so you can update the page
           | incrementally and alpine and stimulus are both tools to add
           | JS sprinkles like you've described in a way that is
           | unobtrusive.
        
             | uuyi wrote:
             | I appreciate the notion but my objective was to do the
             | exact opposite of this and keep away from external
             | dependencies and scripts where possible, apart from the
             | solitary go-sqlite3.
             | 
             | The result is about 30K of source (including Go, CSS, HTML
             | templates) which is less than minified alpinejs!
        
           | CraigJPerry wrote:
           | >> This whole concept could scale to tens of users
           | 
           | I strongly suspect this approach scales to tens of thousands
           | of users. Maybe 30-40k users would be my guess on a garden
           | variety intel i5 desktop from the past 3 years or so.
           | 
           | I say this because that hardware (assuming NVMe storage) will
           | do north of 100k connect + select per second (connect is
           | super cheap in sqlite, you're just opening a local file),
           | assuming 2-3 selects per page serve gets me to the 30-40k
           | number. The http server side won't be the bottleneck unless
           | there's some seriously intensive logic being run.
        
             | uuyi wrote:
             | Interesting point. I may have to write a performance test
             | suite for it now and test this.
        
       | ilovecaching wrote:
       | The vast, vast, vast majority of organizations don't need micro
       | services, don't need half of the products they bought and now
       | have to integrate into their stack, and are simply looking to
       | shave their yak to meet the bullet list of "best practices" for
       | year 202X. Service oriented architectures and micro services
       | solve a particular problem for companies that are operating on a
       | massive scale and can invest (read waste money) on teams devoted
       | to tooling. What most companies should do is build a monolith
       | that makes money, but hire good software engineers that can write
       | packages/modules whatever with high levels of cohesion and loose
       | coupling, so that _one day_ when you become the next Google, it
       | will be less of a pain to break it into services. But in the end
       | it really doesn 't matter if it's painful anyway, because you'll
       | have the money to hire an army of people to do it while the
       | original engineers take their stock and head off to early
       | retirement.
        
         | danielvaughn wrote:
         | I'd never worked with micro-services before this latest
         | freelance project. I start working with this platform that is
         | basically "note taking but with a bit of AI/ML". So okay, a bit
         | of complexity with the ML stuff, but otherwise a standard CRUD
         | app.
         | 
         | The application itself is a total of 3 pages, encompassing
         | maybe 20 endpoints at the most, with about 100 daily active
         | users. For the backend, some genius decided to build a massive
         | kubernetes stack with 74 unique services, which has been
         | costing said company over $1K/month just in infra costs. It
         | took me literally weeks to get comfortable working on the
         | backend, and so much stuff has broken that I have no idea how
         | to fix.
         | 
         | Not only that, but the company has never had more than 1
         | engineer working on it at a time (they're very small even
         | though they've been around a bit). If there were such a thing
         | as developer malpractice, I'd sue whoever built it.
        
           | DerArzt wrote:
           | > 3 pages .... 74 unique services
           | 
           | Just, wat.
           | 
           | Sounds like the architect was doing some resume driven
           | development cause damn.
        
             | danielvaughn wrote:
             | In all honesty I'm _very_ angry about it. It was built a
             | while ago, and the dev is no longer here, but I almost want
             | to track him down and make him help fix it. This founder
             | isn 't technical, so he's been leaning on developers for
             | guidance, and this guy basically built him a skyscraper
             | when what he really needed was a shed. It hurts to think
             | about all the time and money that he's poured into just
             | maintaining it. Crazy.
        
           | ammanley wrote:
           | _74_ services on a k8 stack for _3_ pages and 100 active
           | daily users?????
           | 
           | This has to be a crime.
        
             | danielvaughn wrote:
             | Ok I feel very validated now - I'm not used to
             | microservices so didn't know what was typical. It _felt_
             | crazy, so good to know based on this comment 's responses
             | that it is indeed crazy.
             | 
             | For example, in order to sign up a user...the client hits
             | the /signup endpoint, which first lands on the server-
             | gateway service. Then that is passed along to an account-
             | service which creates the user. Then the accounts-service
             | hits a NATS messaging service twice - one message to send a
             | verification email, and another to create a subscription.
             | The messaging service passes the first message along to the
             | verification-service, which sends out a sendgrid email.
             | Then the second message gets passed along to a
             | subscription-service-worker. The subscription-service-
             | worker adds a job to a queue, which when it gets processed,
             | hits the actual subscription-service, which sends along a
             | request to Stripe to create the customer record and trial
             | subscription.
             | 
             | 6 services in order to sign up a user, in what could have
             | been done with about 100-300 lines of Node.
        
               | Beltalowda wrote:
               | Even the most fanatical microservices proponent will tell
               | you that's just bonkers.
               | 
               | At my very first programming job many years ago I was
               | given a bunch of code written by a string of "previous
               | guys" (mostly interns) over a period of 10 years or more
               | and was told "good luck with it". I was the only
               | developer, with no real technical oversight. It was my
               | first "real" programming job, but I had been programming
               | for many years already (mostly stuff for myself, open
               | source stuff, etc, but never "real" production stuff).
               | 
               | In hindsight, I did some things that were clearly
               | overcomplicated. I had plenty of time, could work on what
               | I wanted, and it was fun to see if I could get the
               | response speed of the webpage down from 100ms to 50ms, so
               | I added a bunch of caching and such that really wasn't
               | needed. Varnish had just been released and I was eager to
               | try it, so I added that too. It was nowhere near the
               | craziness you're describing though, and considering the
               | state of the system when I took things over things were
               | still massively improved, but I'd definitely do things
               | different now because none of that was really needed.
               | 
               | Maybe if it had been today instead of 15 years ago I
               | would have gone full microservice, too.
        
       ___________________________________________________________________
       (page generated 2022-04-06 23:00 UTC)