08 January 2017
As the lead developer on a project, you’ve already either created or been given the high level design by your project’s software architect and will now have to implement it. What sort of goals should you keep in mind and shoot for as you lead development of the project in order to maintain the initial momentum towards a scalable product? Thinking big is still part of the game; you must identify specific challenges and potential or actual bottlenecks which could challenge the long term viability of your web application. Whether that’s performing volume testing on specific and vital endpoints of your application or performance testing some common user flows, you have to be cognizant at all times of areas that could be pain points during the growth of your product.
Here are some actions to take during development:
- Leave a SQL logger running and see if any specific requests generate more queries than you’d expect
- Go wild: Add a million items to a shopping cart, spam likes and comments
- Be evil: Try to break things. Create loops in parent/child categories for instance.
- Add a ton of web processes on a production clone to see how your database handles it (connection pooling/raw resources)
- Perform simple requests with stupid amounts of test data. Accidentally loading all records from your DB anywhere?
- Ensure any services such as Redis or Elasticsearch can handle traffic spikes.
There are many more places to take action and monitor; the above should be a starting point to inspire other actions. What do combinations of the above yield, and how does it apply to your application? Thinking on and answering that will provide new ideas which you can combine with the originals until you’ve synthesized a large amount to take care of and think about. Whether you formalize testing of these or not, always remember that they all revolve around two points. Any endpoints, and user or automated actions could be potential weak spots for exacerbating an unidentified hot spot, so keep these following two in mind:
- Malicious actions (intentional or no)
- Large Amounts of Information (whether data or users)
Be aware and mindful of those two, let them guide you as you review features and perform final testing. A little preemptive action on these will go a long way towards saving you for the day where you get slashdotted or decide to turn your product into a SaaS offering. Covering and catching even the few most likely candidates for slowdowns will save you massive amounts of time later.
04 January 2017
Back in my days at a student, one of my networking professors brought up the same point every time we’d meet for a class: “When you’re implementing something, always ask yourself, ‘How does it scale?’” Over the last few months I’ve spent a lot of time dealing with systems and making sure they scale well using various technologies. I ran into an unexpected on today, though. It was Spree. Yes, the extremely configurable and awesome e-commerce platform does not play well with certain scenarios.
The issue was that whenever an of an order was updated which altered shipping costs, a callback chain was invoked. This either creates or updates shipping information for each item being ordered. What this means for the application is that (in order to remain highly configurable and track everything perfectly) each individual item from an order has a ShippingItem in the database. Buy 10 widgets, you create 10 ShippingItems. Buy 1000 widgets (or even 200), and Heroku will throw H* timeouts, which introduces [other errors with your Spree Addresses] that have to be solved so your users can view in progress orders and continue the checkout process.
The unforunate effect of this is that the more items someone wants to order from your site, the slower it will run. To throw some numbers in, an order with a total quantity of 2000 items took approximately 2 minutes to update the user’s address during the order process on a 1x Heroku dyno. The saving grace here is that the Spree team keeps a Slack channel open, and @brendan was able to point me to a previous issue and fix over at the Spree Github. Even better, dropping it in worked like a charm. Now, it has introduced a few more complications, such as needing to update a handful of template files and adjust a little logic, but in the course of an hour we had large orders running through the customer’s system again.
The point of this post isn’t to point out any weaknesses on Spree’s part, but to highlight the importance of making sure your systems scale. “Your” involves not just the code you write, but also other code being included. More importantly, make sure to test it on a system identical to your production environment. The reason I say that is I’d run many test orders through the system, but had only ever ran large orders through locally, assuming that it wouldn’t make a difference. I’d even noticed the substantially large amount of SQL queries for the checkout process on the area that had ended up hanging. What was different though was that on my development machine those queries completed at least an order of magnitude quicker (I recently timed it, it was ~4s vs the aforementioned 2 minutes). When things like that jump out at you, make a note and be sure to test on production. To quickly touch on it, there is a difference between performance testing and load testing. In this case, a very specific type of load testing was needed; namely checking out with large quantities in the cart.
To go back to what that professor shot our way every class period: Make sure your system scales. This doesn’t mean overly optimize early on or spend an inordinate amount of time testing every aspect of your system, but make sure to get real numbers in as benchmarks and base levels of speed so that you can check in time to time and be sure your application stays healthy and performant. At a bare minimum, fire up Siege and point it at some endpoints, then run through the most common user flows while New Relic or Librato is hooked up and you’ve got a window up tailing the logs so you can be sure to have a mental model and a few specific numbers of where the majority of requests to your application will go.
02 January 2017
The more time I spend developing applications, especially as the number of projects increases, the more I find a shared subset of functionality shared among all of them. Thankfully, using base applications as templates and gems I’ve been able to keep master versions of my shared code. They fall into a few categories:
- Admin Features
- User Features
- Admin Portal
- Invite Only Sign In
- Release Notes/Admin Changelog
- Messaging/notifications to admins
Non-registered User Features
- Markdown renderable posts (blog, articles etc)
- Static (or rarely changed), top-level pages
Registered User Features
- Code Health
- Tree structures (usually part of the categories)
- Dynamic Sitemap
- RSS Feed
As I mentioned above, templates and gems are immensely helpful for getting all this up and running, but customizing and configuring all those things for dozens of apps gets tiresome. There have been multiple conversations with myself arguing pros/cons of spinning up my own fuller featured template (which looks more and more like a real CMS every few weeks) vs the existence of many CMSs. Not to mention the lumbering presence of WordPress. Which, as much as I hate working with it, does have almost every imaginable plugin.
You may ask, “What’s the point of this post?”. The point is this: To lay my thoughts out in the open. I’ve found that often, that’s the quickest way to reach a resolution on what should be done with the limited time resources offered us. Most likely in the upcoming months I’ll either discover a nearly perfect CMS solution, or end up creating my own so that future projects can be spun up even quicker, providing a better value to everyone.
19 December 2016
Even though we’re all capable of building a commenting system from scratch, sometimes public comments for an app are fine and even encouraged (such as enhancing visibility from other sites). I recently added comments to a client’s app and we decided on using Disqus, since it can be dropped in quickly, and all the commented on pages will be public. I’ve written this since the existing Disqus how to’s for Rails which showed up on a google search were outdated; Disqus has since changed their universal js snippet.
The Boilerplate JS
When you want to install Disqus, you register on their site, and they give you a code snippet to insert on every page which you want comments. For this example, we’ve got an
Article model, and when viewing each article, want to display comments.
# app/views/articles/show.html.slim # non relevant erb here = render 'disqus'
At which point we’d insert the vanilla Disqus JS into a partial:
From here, all that’s needed is modifying
disqus_config as follows:
this.shortname = "application_name"; this.page.url = "<%= url_for(host: ENV['APPLICATION_HOST']) %>"; this.page.identifier = 'article-<%= @article.id %>'; this.page.title = '<%= @article.title %>';
At which point, Disqus will load up comments for your page. Easy enough!
The one customization I made to the recommended config is
this.page.identifier. I scoped that to articles, rather than just inserting the article ID, so that in the future if other models needs comments they can be easily separated from the article comments.
Why Disqus Than Roll Your Own?
As I mentioned, there were no privacy concerns going into this (ie, all pages are public facing anyways), plus this will offload maintenance for both comment functionality and administration. This has enabled us to spend more time on items like addressing page load speeds, Elasticsearch enhancements and functionality which is unique to their business. It saves them money, and allows me to work on much more engaging projects, which is a win for everyone.
10 December 2016
When should you work? How often should breaks be scheduled? How about food, water, workouts? What sort of things alter your mood or mindset throughout the day which can alter your quality and output? What follows are some things to think on as you approach the work week. Focus on these items and be productive over the next week.