Perl Dancer conference
Hancock, New York
8 October 2014
What did web application server hosting look like when Interchange was young, 15 years ago?
Around year 2000, a Verio dedicated server with:
$900/month
(It would’ve been cheaper with a young startup called Rackspace.)
2014, a Linode cloud instance with:
$10/month
= $7.24/month inflation-adjusted to year 2000
Pay for blocks of IPv4 addresses. Only.
That Verio server included 32 IP addresses.
Pay for individual IPv4 addresses. Running out.
The Linode VPS includes 1 IPv4 + 1 IPv6 address.
IPv6 is real and adoption is growing.
Google reports 4.5% of all its traffic is now native IPv6.
HTTPS expensive and rare:
HTTPS cheap and commonplace:
Used as a rare and unreliable garnish.
Websites had to work with JavaScript disabled.
Essential.
A year ago, starting with Firefox 23,
Mozilla removed the user-facing option to disable JavaScript.
Optional.
Required.
With bonus annoying policy notifications required by some governments.
HTML5 Storage (aka localStorage) now also widely used.
Apache for most everything.
One child process per connection.
Custom compiled CGI binary to connect Apache to Interchange.
(And good old TUX.)
Apache still alive and well.
nginx growing fast. Event-driven, no forking
or separate processes per connection.
Proxy to heavy-weight Apache or Plack servers.
Use HTTP as the universal glue protocol.
More central to the focus of this conference …
Some things were suited to the times.
Other things we had not yet learned how to do better.
When MiniVend and Interchange were created,
nothing else met all those needs.
It predates CPAN.
In the 2000s, many high-quality CPAN modules started to cover most of those areas.
Except ecommerce, for which Interchange 6 fills a gap.
Building on Dancer and other CPAN modules makes us part of a bigger community, with more activity and better quality.
Less cohesive, but that’s the tradeoff.
Now people are mixing off-the-shelf open source and other applications to make a single website.
People are not willing to reinvent wheels in a single framework and language, but instead expect to mix a CMS, ecommerce component, Q&A or forums or comments features, search and faceting functions, and maybe use separate back-end ERP systems.
Perl has a serious reputation problem.
Help end all-or-nothing thinking by being open to other languages and frameworks.
It’s easier to be open if we don’t feel we have to throw away everything we have to try something new.
Pure Interchange 4/5 frontend + admin.
All customer-facing content served via homegrown CMS
in Interchange using custom database fields.
Entire web space handled by Interchange.
Usual ecommerce functions.
Put code in Subversion, and later Git.
Even production is just a Git checkout.
Catches Interchange admin local changes.
Set up independent development environments.
Here is an early place that DevCamps was used and refined.
Started using regular Perl modules for new development.
Added new subsections of the site on different technologies such as WordPress for blog, which lived on same server and used Apache configuration for some of the URL space.
Better reverse proxying to a different server.
Interchange-based CMS was too inflexible, expensive to change.
Moved customer-facing content to PHP frontend.
Managed by in-house developers who work with us,
and keep code in the same Git repository.
“Git is my CMS.”
Set up customer-facing nginx proxy servers that cache objects from Apache (images, CSS, JavaScript) and Interchange (fully-cacheable pages).
Set default cache lifetime to 2 hours, but can override by directory and leave Interchange uncached.
Adding a CDN in front is then easy and caches at the edge.
Moved cart, checkout, and receipt pages to JavaScript frontend talking to Perl web service living in Interchange 5.
Now uses HTML5 Storage instead of cookies.
Much faster feel for users. Easier to adapt next steps to choices made.
Keep backend ecommerce and database away from PHP. Don’t need more cooks in the kitchen.
GET https://the.site/service/countries/en
{
"timestamp" : "2014-10-08 04:26:04+0000",
"locale" : "en",
"countries" : [
{
"states" : [
{
"name" : "Alabama",
"shippable" : true,
"id" : "AL"
},
{
"name" : "Alaska",
"shippable" : true,
"id" : "AK"
},
// etc.
"name" : "USA",
"shippable" : true,
"id" : "US"
},
{
"states" : [
{
"name" : "Alberta",
"shippable" : true,
"id" : "AB"
},
{
"name" : "British Columbia",
"shippable" : true,
"id" : "BC"
},
// etc.
"name" : "Canada",
"shippable" : true,
"id" : "CA"
},
{
"name" : "France",
"shippable" : true,
"id" : "FR"
},
{
"name" : "Germany",
"shippable" : true,
"id" : "DE"
},
// etc.
{
"name" : "Zimbabwe",
"shippable" : false,
"id" : "ZW"
}
]
}
GET https://the.site/service/products/en/USD
{
"currency" : "USD",
"timestamp" : "2014-10-08 04:35:30+0000",
"locale" : "en",
"processing" : {
"booklet" : "0",
"maximum" : "4",
},
"products" : {
"00238" : {
"now" : {
"effective_from" : "2013-12-26",
"effective_until" : "2015-01-01",
"add_tax" : 0,
"downloadable" : false,
"deliverable" : true,
"units_per_gift_wrap" : 10,
"revision" : "10",
"price" : 389,
"title" : "Super Excellent MegaProduct",
}
},
// etc.
}
}
GET https://the.site/service/shipping/en/USD/2:00007,1:00014
{
"locale" : "en",
"currency" : "USD",
"order_date" : null,
"timestamp" : "2014-10-08 04:41:16+0000",
"shipping" : {
"order_date_is_holiday" : null,
"countries" : [
{
"shipmodes" : [
{
"cost" : 10,
"estimated_delivery_date" : "Thursday, October 16",
"title" : "UPS Ground (7-10 days)",
"code" : "UPSG"
},
{
"cost" : 14,
"estimated_delivery_date" : "Friday, October 10",
"title" : "UPS 2nd Day Air",
"code" : "UPS2DA"
},
],
"processed" : "Wednesday, October 8",
"title" : "Continental US",
"code" : "US"
},
// etc.
],
"order_date_formatted" : "Tuesday, October 7 at 22:41"
},
"cart" : [
{
"sku" : "00007",
"quantity" : 2
},
{
"sku" : "00014",
"quantity" : 1
}
]
}
POST https://the.site/service/order
{
"address1" : "123 Main St.",
"address2" : "",
"affiliate" : null,
"attempt_count" : "1",
"b_address1" : "123 Main St.",
"b_address2" : "",
"b_city" : "Sacramento",
"b_company" : "",
"b_country" : "US",
"b_fname" : "Beloved",
"b_lname" : "Customer",
"b_phone" : "123-456-7890",
"b_state" : "CA",
"b_zip" : "95834",
"campaign" : "",
"city" : "Sacramento",
"company" : "",
"country" : "US",
"credit_card_csc" : "❤❤❤",
"credit_card_expiration_month" : "❤❤",
"credit_card_expiration_year" : "❤❤",
"credit_card_number" : "411111**1111",
"delivery_mode" : "downloadable",
"discount_amount" : "0",
"discount_group" : null,
"discount_handling" : "0",
"email" : "someone@gmail.com",
"fname" : "Beloved",
"handling" : "5",
"lname" : "Customer",
"mail_list" : false,
"payment_method" : "credit_card",
"salestax" : 0,
"shipmode" : "None (downloadable)",
"shipping" : "0",
"state" : "CA",
"subtotal" : "518",
"total_cost" : "523",
"zip" : "95834",
"user_agent" : "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 Safari/537.36",
"orderlines" : [
{
"add_tax" : 0,
"sku" : "00009",
"line_number" : 1,
"subtotal" : 436,
"discount_amount" : 0,
"salestax" : 0,
"quantity" : 4,
"description" : "A Really Awesome Product",
"cutoff_choice" : null,
"price" : 109,
},
// etc.
]
}
With web service-based ordering, more easily automated testing of dozens of kinds of orders:
Continuous integration.
Stop storing encrypted credit cards.
Interchange’s PGP encryption and careful handling of form submissions was good.
PCI DSS still allows it, but increases compliance cost:
Much easier to never store the credit card numbers on your own servers at all.
Avoid public relations disaster in case of a breach.
Even with no card numbers stored on the server, attackers can compromise checkout page and sent card number to their own server.
Move Interchange 5 admin to separate internal-only website on its own subdomain.
Beneficial for security, but also because Interchange 5 admin expects to own the URL space for its /process route, actionmaps, etc.
Moving order web services backend to Dancer web services.
Separate server for each service, to keep them independent and limit dependencies, maximize ease of making changes.
Ongoing database cleanup. We kept the database pretty clean:
Ongoing dead code removal. We do that pretty well too.
What should we have done differently, in hindsight?
We should have introduced Dancer or another modern Perl web service earlier, and proxied to it.
transactions table columns predating Standard Demo of 2004:
nitems | VARCHAR(9) subtotal | VARCHAR(12) shipping | VARCHAR(12) handling | VARCHAR(12) salestax | VARCHAR(12) total_cost | VARCHAR(16) order_date | VARCHAR(32) archived | VARCHAR(1) deleted | VARCHAR(1) complete | VARCHAR(1)
Change to SQL-friendly types:
nitems | INTEGER subtotal | DECIMAL(12,2) shipping | DECIMAL(12,2) handling | DECIMAL(12,2) salestax | DECIMAL(12,2) total_cost | DECIMAL(12,2) order_date | TIMESTAMP WITH TIME ZONE archived | BOOLEAN deleted | BOOLEAN complete | BOOLEAN
We should have upgraded PostgreSQL sooner
and declared UTF-8 encoding.
But then validation kicks in.
Terrifying possibility of data rejection!
“Chinese won’t matter!” … It often ends up mattering.
PCI DSS prefers limiting servers to one function each.
That makes virtualization the affordable way to go for small functions.
Migrating is a pain. But you get a lot in return:
Should be as close to identical to production as possible.
Mostly similar, but don’t have nginx caching in front of camps yet.
Camp system not keeping up. Need to replatform.
Things aren’t necessarily better just because they’re newer.
But many newer things are better, having been refined based on lessons of past mistakes, and improved hardware and software available today.
Sometimes we have to take on technical debt to get a project off the ground, to survive. That’s ok.
Software requires maintenance similar to houses, cars, airplanes.
It’s a judgement call how much or little to invest, how often, but “none” is not a wise way to respect your investment.
We must take on the hard task of refactoring what we have, and improve it piece by piece.
If we shirk that duty, our code will atrophy and rot and become increasingly difficult to grow, fix bugs in, or bring new developers into.
Often we blame technology for such software rot.
No framework or programming language can prevent bit-rot.
New technologies’ advantage: haven’t been around long enough for code built on them to decay yet.
We can’t do it all ourselves:
We are that meat.
Twitter: @jonjensen0
Email: jon@endpointdev.com