Originally coined in 1998 during the “dot-com” bubble, I had thought that the term “LAMP” had faded with developers in the intervening decades with the rise of language-specific web frameworks for:
Granted, PHPstill relies on web server-specific modules, APIs, or variations of the FastCGI protocol for interfacing with a web server. And Python web applications typically make use of its WSGI protocol either as a web server extension or, like the Perl examples above, as a proxiedstandalone server. But all of these are deployment details and do little to describe how developers implement and extend a web application’s structure.
Note how the various four-letter JavaScript stacks (e.g., MERN, MEVN, MEAN, PERN) differentiate themselves mostly by frontend framework (e.g., Angular, React, Vue.js) and maybe by the (relational or NoSQL) database (e.g., MongoDB, MySQL, PostgreSQL). All however seem standardized on the Node.js runtime and Express backend web framework, which could, in theory, be replaced with non-JavaScript options like the more mature LAMP-associated languages and frameworks. (Or if you prefer languages that don’t start with “P”, there’s C#, Go, Java, Ruby, etc.)
My point is that “LAMP” as the name of a web development stack has outlived its usefulness. It’s at once too specific (about operating system and web server details that are often abstracted away for developers) and too broad (covering three separate programming languages and not the frameworks they favor). It also leaves out other non-JavaScript back-end languages and their associated frameworks.
The question is: what can replace it? I’d propose “NoJS” as reminiscent of “NoSQL,” but that inaccurately excludes JavaScript from its necessary role in the front-end. “NJSB” doesn’t exactly roll off the tongue, either, and still has the same ambiguity problem as “LAMP.”
How about pithy sort-of-acronyms patterned like database-frontend-backend? Here are some Perl examples:
MRDancer: MySQL, React, and Dancer (I use this at work. Yes, the M could also stand for MongoDB. Naming things is hard.)
MRMojo: MongoDB, React, and Mojolicious
PACat: PostgreSQL, Angular, and Catalyst
etc.
Ultimately it comes down to community and industry adoption. If you’re involved with back-end web development, please let me know in the comments if you agree or disagree that “LAMP” is still a useful term, and if not, what should replace it.
12 thoughts on “LAMP is dead! Long live (Perl) web frameworks!”
Ah the conundrum of changing technologies! Given the correct skill set anyone can write just about anything using any set of technologies. Question is does the end result meet the business objectives. I go back to Roller Coaster Tycoon 1: http://www.chrissawyergames.com/faq3.htm. Yeah it was a success and well done.
Do you recommend Dancer with React ?
It’s certainly capable enough, and has plugins for AJAX routes and RESTful web services. Though for the latter as microservices I tend to favor Mojolicious and its OpenAPI plugin. I wrote a small two-part tutorial about that here and here.
I don’t entirely agree. I recently used Mojolicious for a project and it sits behind an nginx reverse proxy. My implementation might be a little weird but it works surprisingly well with nginx server side includes. Most of the web applications that I run are served via an nginx reverse proxy. As you already mentioned, PHP is still tightly coupled with traditional web server software so that remains yet another reason for me to use this stack.
The acronym has possibly outlived its usefulness when taken literally. OpenBSD, nginx, and PostgreSQL have replaced Linux, Apache, and MySQL on my end but the concept is still fundamentally the same.
[…] LAMP is dead! Long live (Perl) web frameworks! […]
My current $WORK project would be classified as M‑P: MySQL, Perl. No web frame work. We had one in the beginning, cut it out, saw the 50% drop in response time, and never looked back. It also uses Linux and ngix, but replacing them with something else would be easy. (And so would be replacing MySQL with another relational database, almost all my SQL confirms to the standard).
But $WORK would not mind rewriting it in Java (but I do).
Ahhh, the Perl fashion industry is alive and well! Yes, pay attention — you need to forget about that Perl LAMP application that has 50,000+ hours of development time that you have been running over 19 years and executes flawlessly while making lots of money at the same time for your company! It’s time to throw it all away and rewrite everything because yet-another-person™ in the Perl community thinks that you are out of step with the “times”. Puleeeze don’t tell me you are still using CGI, Mod Perl, and LAMP! That’s the Perl hall of shame!!! Only a [insert appropriate demeaning noun] would use those ancient tools to support applications.
Early on (mid-90s to early 00s), the Perl community ran on TMTOWTDI (there is more than one way to do it). We supported each other and worked to improve things. Sure, new things came out, but we never declared legacy code as inadequate or insufficient because some new module could accomplish the same thing. But we got rid of that community cohesion a long time ago and replaced it with TINWTDI (there is no way to do it) because we realized that everyone that does not agree with us is an idiot and can’t possibly understand how important [insert new feature] is.
So throw it all way and rewrite it again, and soon you too can strut down the Perl fashion runway and reveal the glory of all your pretty feathers! Besides, who the hell wants to enhance features in a working application to make more money when you can refactor your code to support the latest what-ever-it-is over and over and over again?
IMO, if we spent half of much time fixing the legacy code that made Perl great in the first place, rather than inventing yet another object system, creating yet-another magical framework, or removing yet-another feature used in the code base by thousands of legacy apps, we probably would not be on this accelerated path to irrelevance and potential demise. But then again, I have never been much of a fashionista. Besides, I prefer “works well, well written, and maintainable” to “shiny new thing, you’re an idiot if you jump on this new thing” any day.
I’m glad the C programming community does not have the same propensity to constantly throw the old away and replace it with yet-another new thing like the Perl community does. I’ll crawl back under my rock now …
How about when the Business wants yet another new feature on the CGI which has been slowly accumulating features over the last 19 years? Don’t break it because the business depends on it to keep making lots of money! Can you even unit test it?
Sure, you’ll never get rid of it because it embeds too much business logic to untangle, but even the resident masochist wouldn’t write anything new in CGI.
I can’t believe you left out the JERK stack! (Javascript, Express, React and Kubernetes (of course)) lol
20 years ago I started building a business solution (CRM+ERP) in Perl (LAMP stack) on CentOS. I’ve been maintaining this system in isolation, without much learning or any guidance, mostly repeating simple coding basics a thousand times, using a linux terminal and vim 🙂 Today it looks like this: 60 library files (.pl), 175,000 lines of code, 1576 functions (subs), 374 global variables(!!). I also don’t use any frameworks, because the system has it’s own internal framework for rapid development and UI.
As mentioned above, how do you remodel such a complex, live system? I’ve been hoping to modernise it, but its so delicate to change business logic and a framework that evolved over 20 years.
Lately I’ve been focussing on building tools to help improve the quality of the code. Eg. before you apply a patch, the changes gets analysed and potential issues highlighted. I’m also restructuring the code and adding better error/exception reporting. Beyond that it’s an adventure. It’s daunting to even imagine migrating to a new model or language (also — I love Perl :). I’ll continue to try improve the bad and add the good, but its a journey. I’ve have to grow and learn — challenging stuff.
I hope Perl will be around for many years to come.
Oh, it will be around. As far as your issue is concerned, check out a book on dealing with legacy code like Peter J. Scott’s Perl Medic, which although dated, contains good suggestions on adding tests and gradually restructuring. You don’t have to use a framework, and there are CPAN modules that will help you, e.g., adapting CGI scripts to PSGI applications.
{"id":"11","mode":"button","open_style":"in_modal","currency_code":"USD","currency_symbol":"$","currency_type":"decimal","blank_flag_url":"https:\/\/phoenixtrap.com\/wp-content\/plugins\/tip-jar-wp\/\/assets\/images\/flags\/blank.gif","flag_sprite_url":"https:\/\/phoenixtrap.com\/wp-content\/plugins\/tip-jar-wp\/\/assets\/images\/flags\/flags.png","default_amount":500,"top_media_type":"featured_image","featured_image_url":"https:\/\/phoenixtrap.com\/wp-content\/uploads\/2021\/02\/image-200x200.jpg","featured_embed":"","header_media":null,"file_download_attachment_data":null,"recurring_options_enabled":true,"recurring_options":{"never":{"selected":true,"after_output":"One time only"},"weekly":{"selected":false,"after_output":"Every week"},"monthly":{"selected":false,"after_output":"Every month"},"yearly":{"selected":false,"after_output":"Every year"}},"strings":{"current_user_email":"","current_user_name":"","link_text":"Leave a tip!","complete_payment_button_error_text":"Check info and try again","payment_verb":"Pay","payment_request_label":"The Phoenix Trap","form_has_an_error":"Please check and fix the errors above","general_server_error":"Something isn't working right at the moment. Please try again.","form_title":"The Phoenix Trap","form_subtitle":"Do you like what you see? Leave a one-time or recurring tip!","currency_search_text":"Country or Currency here","other_payment_option":"Other payment option","manage_payments_button_text":"Manage your payments","thank_you_message":"Thank you for being a supporter!","payment_confirmation_title":"The Phoenix Trap","receipt_title":"Your Receipt","print_receipt":"Print Receipt","email_receipt":"Email Receipt","email_receipt_sending":"Sending receipt...","email_receipt_success":"Email receipt successfully sent","email_receipt_failed":"Email receipt failed to send. Please try again.","receipt_payee":"Paid to","receipt_statement_descriptor":"This will show up on your statement as","receipt_date":"Date","receipt_transaction_id":"Transaction ID","receipt_transaction_amount":"Amount","refund_payer":"Refund from","login":"Log in to manage your payments","manage_payments":"Manage Payments","transactions_title":"Your Transactions","transaction_title":"Transaction Receipt","transaction_period":"Plan Period","arrangements_title":"Your Plans","arrangement_title":"Manage Plan","arrangement_details":"Plan Details","arrangement_id_title":"Plan ID","arrangement_payment_method_title":"Payment Method","arrangement_amount_title":"Plan Amount","arrangement_renewal_title":"Next renewal date","arrangement_action_cancel":"Cancel Plan","arrangement_action_cant_cancel":"Cancelling is currently not available.","arrangement_action_cancel_double":"Are you sure you'd like to cancel?","arrangement_cancelling":"Cancelling Plan...","arrangement_cancelled":"Plan Cancelled","arrangement_failed_to_cancel":"Failed to cancel plan","back_to_plans":"\u2190 Back to Plans","update_payment_method_verb":"Update","sca_auth_description":"Your have a pending renewal payment which requires authorization.","sca_auth_verb":"Authorize renewal payment","sca_authing_verb":"Authorizing payment","sca_authed_verb":"Payment successfully authorized!","sca_auth_failed":"Unable to authorize! Please try again.","login_button_text":"Log in","login_form_has_an_error":"Please check and fix the errors above","uppercase_search":"Search","lowercase_search":"search","uppercase_page":"Page","lowercase_page":"page","uppercase_items":"Items","lowercase_items":"items","uppercase_per":"Per","lowercase_per":"per","uppercase_of":"Of","lowercase_of":"of","back":"Back to plans","zip_code_placeholder":"Zip\/Postal Code","download_file_button_text":"Download File","input_field_instructions":{"tip_amount":{"placeholder_text":"How much would you like to tip?","initial":{"instruction_type":"normal","instruction_message":"How much would you like to tip? Choose any currency."},"empty":{"instruction_type":"error","instruction_message":"How much would you like to tip? Choose any currency."},"invalid_curency":{"instruction_type":"error","instruction_message":"Please choose a valid currency."}},"recurring":{"placeholder_text":"Recurring","initial":{"instruction_type":"normal","instruction_message":"How often would you like to give this?"},"success":{"instruction_type":"success","instruction_message":"How often would you like to give this?"},"empty":{"instruction_type":"error","instruction_message":"How often would you like to give this?"}},"name":{"placeholder_text":"Name on Credit Card","initial":{"instruction_type":"normal","instruction_message":"What is the name on your credit card?"},"success":{"instruction_type":"success","instruction_message":"Enter the name on your card."},"empty":{"instruction_type":"error","instruction_message":"Please enter the name on your card."}},"privacy_policy":{"terms_title":"Terms and conditions","terms_body":null,"terms_show_text":"View Terms","terms_hide_text":"Hide Terms","initial":{"instruction_type":"normal","instruction_message":"I agree to the terms."},"unchecked":{"instruction_type":"error","instruction_message":"Please agree to the terms."},"checked":{"instruction_type":"success","instruction_message":"I agree to the terms."}},"email":{"placeholder_text":"Your email address","initial":{"instruction_type":"normal","instruction_message":"What is your email address?"},"success":{"instruction_type":"success","instruction_message":"Enter your email address"},"blank":{"instruction_type":"error","instruction_message":"Enter your email address"},"not_an_email_address":{"instruction_type":"error","instruction_message":"Make sure you have entered a valid email address"}},"note_with_tip":{"placeholder_text":"Your note here...","initial":{"instruction_type":"normal","instruction_message":"Attach a note to your tip (optional)"},"empty":{"instruction_type":"normal","instruction_message":"Attach a note to your tip (optional)"},"not_empty_initial":{"instruction_type":"normal","instruction_message":"Attach a note to your tip (optional)"},"saving":{"instruction_type":"normal","instruction_message":"Saving note..."},"success":{"instruction_type":"success","instruction_message":"Note successfully saved!"},"error":{"instruction_type":"error","instruction_message":"Unable to save note note at this time. Please try again."}},"email_for_login_code":{"placeholder_text":"Your email address","initial":{"instruction_type":"normal","instruction_message":"Enter your email to log in."},"success":{"instruction_type":"success","instruction_message":"Enter your email to log in."},"blank":{"instruction_type":"error","instruction_message":"Enter your email to log in."},"empty":{"instruction_type":"error","instruction_message":"Enter your email to log in."}},"login_code":{"initial":{"instruction_type":"normal","instruction_message":"Check your email and enter the login code."},"success":{"instruction_type":"success","instruction_message":"Check your email and enter the login code."},"blank":{"instruction_type":"error","instruction_message":"Check your email and enter the login code."},"empty":{"instruction_type":"error","instruction_message":"Check your email and enter the login code."}},"stripe_all_in_one":{"initial":{"instruction_type":"normal","instruction_message":"Enter your credit card details here."},"empty":{"instruction_type":"error","instruction_message":"Enter your credit card details here."},"success":{"instruction_type":"normal","instruction_message":"Enter your credit card details here."},"invalid_number":{"instruction_type":"error","instruction_message":"The card number is not a valid credit card number."},"invalid_expiry_month":{"instruction_type":"error","instruction_message":"The card's expiration month is invalid."},"invalid_expiry_year":{"instruction_type":"error","instruction_message":"The card's expiration year is invalid."},"invalid_cvc":{"instruction_type":"error","instruction_message":"The card's security code is invalid."},"incorrect_number":{"instruction_type":"error","instruction_message":"The card number is incorrect."},"incomplete_number":{"instruction_type":"error","instruction_message":"The card number is incomplete."},"incomplete_cvc":{"instruction_type":"error","instruction_message":"The card's security code is incomplete."},"incomplete_expiry":{"instruction_type":"error","instruction_message":"The card's expiration date is incomplete."},"incomplete_zip":{"instruction_type":"error","instruction_message":"The card's zip code is incomplete."},"expired_card":{"instruction_type":"error","instruction_message":"The card has expired."},"incorrect_cvc":{"instruction_type":"error","instruction_message":"The card's security code is incorrect."},"incorrect_zip":{"instruction_type":"error","instruction_message":"The card's zip code failed validation."},"invalid_expiry_year_past":{"instruction_type":"error","instruction_message":"The card's expiration year is in the past"},"card_declined":{"instruction_type":"error","instruction_message":"The card was declined."},"missing":{"instruction_type":"error","instruction_message":"There is no card on a customer that is being charged."},"processing_error":{"instruction_type":"error","instruction_message":"An error occurred while processing the card."},"invalid_request_error":{"instruction_type":"error","instruction_message":"Unable to process this payment, please try again or use alternative method."},"invalid_sofort_country":{"instruction_type":"error","instruction_message":"The billing country is not accepted by SOFORT. Please try another country."}}}},"fetched_oembed_html":false}
Ah the conundrum of changing technologies! Given the correct skill set anyone can write just about anything using any set of technologies. Question is does the end result meet the business objectives. I go back to Roller Coaster Tycoon 1: http://www.chrissawyergames.com/faq3.htm. Yeah it was a success and well done.
Do you recommend Dancer with React ?
It’s certainly capable enough, and has plugins for AJAX routes and RESTful web services. Though for the latter as microservices I tend to favor Mojolicious and its OpenAPI plugin. I wrote a small two-part tutorial about that here and here.
I don’t entirely agree. I recently used Mojolicious for a project and it sits behind an nginx reverse proxy. My implementation might be a little weird but it works surprisingly well with nginx server side includes. Most of the web applications that I run are served via an nginx reverse proxy. As you already mentioned, PHP is still tightly coupled with traditional web server software so that remains yet another reason for me to use this stack.
The acronym has possibly outlived its usefulness when taken literally. OpenBSD, nginx, and PostgreSQL have replaced Linux, Apache, and MySQL on my end but the concept is still fundamentally the same.
[…] LAMP is dead! Long live (Perl) web frameworks! […]
My current $WORK project would be classified as M‑P: MySQL, Perl. No web frame work. We had one in the beginning, cut it out, saw the 50% drop in response time, and never looked back. It also uses Linux and ngix, but replacing them with something else would be easy. (And so would be replacing MySQL with another relational database, almost all my SQL confirms to the standard).
But $WORK would not mind rewriting it in Java (but I do).
Ahhh, the Perl fashion industry is alive and well! Yes, pay attention — you need to forget about that Perl LAMP application that has 50,000+ hours of development time that you have been running over 19 years and executes flawlessly while making lots of money at the same time for your company! It’s time to throw it all away and rewrite everything because yet-another-person™ in the Perl community thinks that you are out of step with the “times”. Puleeeze don’t tell me you are still using CGI, Mod Perl, and LAMP! That’s the Perl hall of shame!!! Only a [insert appropriate demeaning noun] would use those ancient tools to support applications.
Early on (mid-90s to early 00s), the Perl community ran on TMTOWTDI (there is more than one way to do it). We supported each other and worked to improve things. Sure, new things came out, but we never declared legacy code as inadequate or insufficient because some new module could accomplish the same thing. But we got rid of that community cohesion a long time ago and replaced it with TINWTDI (there is no way to do it) because we realized that everyone that does not agree with us is an idiot and can’t possibly understand how important [insert new feature] is.
So throw it all way and rewrite it again, and soon you too can strut down the Perl fashion runway and reveal the glory of all your pretty feathers! Besides, who the hell wants to enhance features in a working application to make more money when you can refactor your code to support the latest what-ever-it-is over and over and over again?
IMO, if we spent half of much time fixing the legacy code that made Perl great in the first place, rather than inventing yet another object system, creating yet-another magical framework, or removing yet-another feature used in the code base by thousands of legacy apps, we probably would not be on this accelerated path to irrelevance and potential demise. But then again, I have never been much of a fashionista. Besides, I prefer “works well, well written, and maintainable” to “shiny new thing, you’re an idiot if you jump on this new thing” any day.
I’m glad the C programming community does not have the same propensity to constantly throw the old away and replace it with yet-another new thing like the Perl community does. I’ll crawl back under my rock now …
How about when the Business wants yet another new feature on the CGI which has been slowly accumulating features over the last 19 years? Don’t break it because the business depends on it to keep making lots of money! Can you even unit test it?
Sure, you’ll never get rid of it because it embeds too much business logic to untangle, but even the resident masochist wouldn’t write anything new in CGI.
I can’t believe you left out the JERK stack! (Javascript, Express, React and Kubernetes (of course)) lol
20 years ago I started building a business solution (CRM+ERP) in Perl (LAMP stack) on CentOS. I’ve been maintaining this system in isolation, without much learning or any guidance, mostly repeating simple coding basics a thousand times, using a linux terminal and vim 🙂 Today it looks like this:
60 library files (.pl), 175,000 lines of code, 1576 functions (subs), 374 global variables(!!).
I also don’t use any frameworks, because the system has it’s own internal framework for rapid development and UI.
As mentioned above, how do you remodel such a complex, live system? I’ve been hoping to modernise it, but its so delicate to change business logic and a framework that evolved over 20 years.
Lately I’ve been focussing on building tools to help improve the quality of the code. Eg. before you apply a patch, the changes gets analysed and potential issues highlighted. I’m also restructuring the code and adding better error/exception reporting. Beyond that it’s an adventure. It’s daunting to even imagine migrating to a new model or language (also — I love Perl :). I’ll continue to try improve the bad and add the good, but its a journey. I’ve have to grow and learn — challenging stuff.
I hope Perl will be around for many years to come.
Oh, it will be around. As far as your issue is concerned, check out a book on dealing with legacy code like Peter J. Scott’s Perl Medic, which although dated, contains good suggestions on adding tests and gradually restructuring. You don’t have to use a framework, and there are CPAN modules that will help you, e.g., adapting CGI scripts to PSGI applications.