CINXE.COM
Modern Perl Programming
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" id="sixapart-standard"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta http-equiv="content-language" content="en-us" /> <meta name="generator" content="Movable Type 4.38" /> <meta name="description" content="ModernPerlBooks.com -- how Modern Perl helps how great programmers solve their problems quickly and effectively." /> <link rel="stylesheet" href="/mt/styles.css" type="text/css" /> <link rel="stylesheet" href="/mt/modernperl.css" type="text/css" /> <link rel="start" href="/mt/" title="Home" /> <link rel="alternate" type="application/atom+xml" title="Recent Entries" href="/mt/atom.xml" /> <link rel="alternate" type="application/atom+xml" title="Recent Comments" href="/mt/comments.xml" /> <script type="text/javascript" src="/mt/mt.js"></script> <!-- <rdf:RDF xmlns="http://web.resource.org/cc/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <Work rdf:about="/mt/"> <dc:title>Modern Perl Programming</dc:title> <dc:description>Perl programming for the modern Perl programmer. </dc:description> <license rdf:resource="http://creativecommons.org/licenses/by-nc-sa/3.0/us/" /> </Work> <License rdf:about="http://creativecommons.org/licenses/by-nc-sa/3.0/us/"> </License> </rdf:RDF> --> <link rel="EditURI" type="application/rsd+xml" title="RSD" href="/mt/rsd.xml" /> <title>Modern Perl Programming</title> </head> <body id="mt-blog" class="mt-main-index layout-wt"> <div id="container"> <div id="container-inner"> <div id="header"> <div id="header-inner"> <div id="header-content"> <h1 id="header-name"><a href="/mt/" accesskey="1">Modern Perl Programming</a></h1> <h2 id="header-description">Perl programming for the modern Perl programmer. </h2> </div> </div> </div> <div id="content"> <div id="content-inner"> <div id="alpha"> <div id="alpha-inner"> <div id="entry-578" class="entry-asset asset hentry"> <div class="asset-header"> <h2 class="asset-name entry-title"><a href='/mt/2014/05/modern-perl-2014-electronic-editions-released' rel='bookmark'>Modern Perl: 2014 Electronic Editions Released</a></h2> <div class="asset-meta"> <span class="byline"> By <span class="vcard author"><a class="fn url" href="http://www.wgz.org/~chromatic">chromatic</a></span> on <abbr class="published" title="2014-05-31T06:00:01-08:00">May 31, 2014 6:00 AM</abbr> </span> </div> </div> <div class="asset-content entry-content"> <div class="asset-body"> <p>This took longer than it should have, but the electronic reader editions of <a href="http://onyxneon.com/books/modern_perl/">Modern Perl: 2014 Edition</a> are available. The letter and A4 PDFs have been available for a while, but we're satisfied with the formatting of the ePub and Mobi (Kindle) versions, so they're now ready for download and redistribution.</p> <p>You can still, of course, <a href="http://www.amazon.com/gp/product/1680500880?ie=UTF8&tag=mp1234-20">buy the print edition of Modern Perl</a> online. You'll get the Kindle edition from Amazon for free there, if you like. If you've already purchased a Kindle edition, it should update for you automatically.</p> <p>It'll take a couple of days for Amazon to approve the new Kindle edition.</p> <p>Please share with your friends, coworkers, and anyone who ought to learn how Perl really works and how to write Perl well in 2014.</p> <p>(It wouldn't hurt my feelings if you shared a link to that page, though it also wouldn't hurt my feelings if you shared with everyone you know Trendshare's <a href="https://trendshare.org/how-to-invest">how to invest guide</a> either.)</p> </div> </div> <div class="asset-footer"></div> </div> <div id="entry-577" class="entry-asset asset hentry"> <div class="asset-header"> <h2 class="asset-name entry-title"><a href='/mt/2014/04/modern-perl-2014-edition-is-out' rel='bookmark'>Modern Perl: 2014 Edition is Out</a></h2> <div class="asset-meta"> <span class="byline"> By <span class="vcard author"><a class="fn url" href="http://www.wgz.org/~chromatic">chromatic</a></span> on <abbr class="published" title="2014-04-04T06:00:01-08:00">April 4, 2014 6:00 AM</abbr> </span> </div> </div> <div class="asset-content entry-content"> <div class="asset-body"> <p>With great pleasure do I announce <a href="http://onyxneon.com/books/modern_perl/">Modern Perl: 2014 edition</a> is out and available in stores. You can also <a href="http://modernperlbooks.com/books/modern_perl_2014/">read Modern Perl: 2014 edition free online</a>. Of course it's available from the various booksellers:</p> <p><a href="http://www.powells.com/cgi-bin/partner?partner_id=30019&cgi=product&isbn=9780985451943">Modern Perl at Powell's</a><br /><a href="http://search.barnesandnoble.com/booksearch/isbninquiry.asp?EAN=9780985451943">Modern Perl at B&N</a><br /><a href="http://www.amazon.com/gp/product/1680500880?ie=UTF8&tag=mp1234-20">Modern Perl at Amazon</a></p> <p>PDFs and other electronic versions are available on the <a href="http://onyxneon.com/books/modern_perl">Onyx Neon Modern Perl book page</a> now. The Kindle edition will update soon.</p> <p>Please spread the word far and wide.</p> </div> </div> <div class="asset-footer"></div> </div> <div id="entry-576" class="entry-asset asset hentry"> <div class="asset-header"> <h2 class="asset-name entry-title"><a href='/mt/2014/02/the-mid-career-crisis-of-the-perl-programmer' rel='bookmark'>The Mid-Career Crisis of the Perl Programmer</a></h2> <div class="asset-meta"> <span class="byline"> By <span class="vcard author"><a class="fn url" href="http://www.wgz.org/~chromatic">chromatic</a></span> on <abbr class="published" title="2014-02-21T09:00:01-08:00">February 21, 2014 9:00 AM</abbr> </span> </div> </div> <div class="asset-content entry-content"> <div class="asset-body"> <p><em><a href="http://news.ycombinator.com/">HN</a> tl;dr: innovation in the 2010s means getting your anonymized social network for marmots acquired so that big SV can put ads on it or datamine your users</em></p> <p><em><a href="http://reddit.com/r/programming">proggit</a> tl;dr: you should have spent your time learning how to write a CRUD app and a mediocre template system, dependency injection framework, and ORM in <strike>Haskell</strike> <strike>Rails</strike> <strike>Erlang</strike> <strike>Scala</strike> <strike>Rails</strike> <strike>Python</strike> <strike>Node</strike> <strike>Clojure</strike> Julia instead of mastering one tool and learning how to solve problems.</em></p> <h2>It Begins</h2> <p>I shut down my music career in 1998. "Career" is too generous a word. I'd made money as a musician, but not enough to call myself a professional and certainly not enough to pay the rent <em>and</em> buy food. I talked my way into the Color LaserJet group at HP.</p> <p>I wrote some code for the printer group, just because I could. I had a little AWT app (even though Swing had just come out, it wouldn't run on Linux for quite a while) for customer support techs to record data about customer problems. Then the networking group asked me to write a program to email customers whenever the web site had a new technical article published. The ten line shell script (written in whatever HP-UX 9.x supported out of the box) was orders of magnitude less than the Java version I could never convince myself to finish.</p> <p>When I realized that I enjoyed playing with HP-UX and the Linux box under my desk far more than I appreciated learning all of the ways that paper could jam or PostScript drivers could render things wrong, I switched to system administration. (Programming was in a different state altogether, and my music degree hardly compared to the CS degree they wanted.)</p> <p>After fighting fires for three months and automating away the remaining problems, I had a lot of time on my hands. The web was suddenly very interesting. mod_perl had just come out and applets were obviously not working. I picked up Perl.</p> <p>I started a couple of small free software projects on my own. They never went anywhere. (Everyone used to write a template system in those days. Then I found the source code to <a href="http://everything2.com/">Everything 2</a>'s predecessor, sent in a couple of patches, and (after Carly Fiorina decided to save money by not paying system administrators) eventually started working for the dot com that produced <a href="http://perlmonks.org/">Perl Monks</a> and promptly imploded.</p> <p>Then I wrote a book. It had a cool cover but you probably didn't read it. (It was only the first book in English about blogging, but the title was wrong anyhow. This was the last gasp of the first dot com era, when everything seemed like a good idea in a certain context, almost including the TSA.)</p> <p>Writing a book is equal parts frantic research and tedium, and in the first of many moves designed to convince myself that my actions had a positive effect on a cruel and uncaring universe, I took up Michael Schwern's call to unify the internals of <a href="http://search.cpan.org/perldoc?Test::Simple">Test::Simple</a> and <a href="http://search.cpan.org/perldoc?Test::More">Test::More</a>. The result is <a href="http://search.cpan.org/perldoc?Test::Builder">Test::Builder</a>.</p> <p>This was a revolution for everyone on the perl-qa list and it was a revolution for Perl in general, but it's the kind of revolution that you look back on now and think "Wow, that was obvious!" and "That was a revolution?" because it seems like such widespread common sense, like "It's good to drink water!" and "Maybe smoking has deleterious health effects."</p> <p>It was a revolution in that it let a thousand blossoms of test modules bloom (so people could solve their own specific testing problems and share those solutions) while providing the soil in which all of those blossoms could bloom (so they all worked together with almost no effort on the module writer's part). This is an important principle of API design: handle the difficult, tedious, and tricky parts at the low levels so that everyone gets that right while not ruling out people doing their own things at higher levels. If that makes you think of layering, you're right.</p> <p>I did some consulting work for a while, based on the strength of my free software work with Perl. (Making a name for yourself answering questions on a site does help get your name in the community and build some networking connections. Getting a patch or two in a dominant project helps as well.)</p> <p>Then my career took a lurch sideways, and I spent some time as an editor concentrating on free software and technology.</p> <h2>Free Software as a Hobby</h2> <p>It was important to keep my programming skills fresh, so I increased the amount of time spent contributing to free software projects (in and around writing books). <a href='/mt/2013/02/goodnight-parrot'>Parrot sucked up a lot of my efforts</a>.</p> <p>(More than one Parrot/Rakudo/P6 developer said to me in separate conversations as far back as 2007 that "Sure, it's a long slog now and no one's getting compensated much for it, but when it's done in the next 18 months, there'll be a lot of interest in books/training/consulting, and we'll be in a good position then!")</p> <p>This was a rough time to be in Perl. Having helped push the Ruby snowball back down the hill with <a href="http://oreilly.com/ruby/archive/rails.html">Rolling with Ruby on Rails</a> (just over nine years ago now!) and with Perl stuck on 5.8 for another almost three years, the hope was still that a new major version would turn the language's fortunes around. Meanwhile, the testing culture had infected the core language as well as the CPAN like an unseen brigade of healthy gut fauna (don't leave the dinner table without it). There was growth and roots and foundations, but the effects would take some time.</p> <p>Pugs helped, but Pugs has been a historical curiosity for a long time now.</p> <h2>The Resurgence of Perl that Nobody Noticed</h2> <p>While the world stopped waiting for a new major version of Perl, Perl 5 came out with several new major versions and the CPAN continued to be the reason that I stuck with Perl. I'm not alone in this. <a href="http://moose.perl.org/">Moose</a> is probably the singular reason there are any significant new projects in Perl in 2014. This by no means should take credit away from other projects such as <a href="http://search.cpan.org/perldoc?DBIx::Class">DBIx::Class</a>, <a href="http://mojolicio.us/">Mojolicious</a>, <a href="http://catalystframework.org/">Catalyst</a>, and other projects well worth mentioning, but Moose was a revolution in the noisy, flashy, overthrow the dominant paradigm sense. It was Fidel to the rest of the CPAN's Che—the one that gets the credit, unless you're some sort of weird countercultural rebel who likes giving oligopolic corporations money to wear t-shirts with your icon's image on them. (That metaphor rode away from my point on a motorcycle tour of South America. Sorry.)</p> <p>That revolution needed a name.</p> <h2>Modern Perl</h2> <p><a href="http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742?tag=mp1234-20">JavaScript had its resurgence</a>, going from "that awful language that everyone has to use and nobody likes" to "Sure, if you squint hard enough and ignore all of this awfulness over here and are very very disciplined, you can write code that's not too bad". (If that apologia sounds familiar and you feel bitter, keep in mind that, unlike Perl, <em>you have no choice whether to use JavaScript on the client side</em>.)</p> <p>I figured this was a great time to leave the increasingly depressing world of corporate technical book publishing to work on <a href="http://onyxneon.com/">my own technical book publisher</a>, because it didn't cost us tens of thousands of dollars to get the first copy out the door. While it's nice that the revenue we make on a book like <a href="http://www.amazon.com/gp/product/0977920178?ie=UTF8&tag=mp1234-20">Modern Perl</a>, it's still a lot better to think of writing or publishing a book as "a hobby that'll buy you nice dinners here and there" instead of "I will never have to work again, unless you count sleeping on a pile of gold coins to be work".</p> <p>Book publishing is driven by the semi-random lightning strikes of big hits, and you have to be in the right place at the right time. Like a poker player, you need to know when to go all in—and, more important, you need to know when to get out. This is one of the reasons the tech book publishing market is so awful. Ruby and Rails were the hottest things since JSP in 2005 and 2006, but the market had reached its peak of sales in 2007 even as publishers were still pumping out the kind of ancillary books around Rails that they'd pumped out around Perl in 1998, 1999, 2000, and 2001 even as talking puppets, razorfish, and drones delivering fresh vegetables even before you one-click order them had become a bubble about to pop, taking with it countless jobs and lots of paper money in an inflated stock market. (Maybe that's what you should expect from hiring a member of an adolescent power fantasy fiction cult to manage the world's largest economy, but keep in mind music degree not economics degree and everything's more obvious in retrospect.)</p> <p>That's one reason why there hasn't been a <em>Modern Perl Testing</em> book; I can't convince myself to give up increasingly valuable weekends and evenings when there are other things I could do which are either much more relaxing (spending time with friends and family), much healthier (cooking, exercising), or profitable (investing). At some point, there <em>might</em> be a <em>Programming Rust</em> book, if only because I find <a href="http://rust-lang.org/">Rust</a> fascinating for what it both does and doesn't do. I wish I'd had it ten years ago for Parrot.</p> <p>With that said, writing and publishing <em>Modern Perl</em> was a good thing. Giving it away for free was a good thing. Maybe I would have made as much revenue if I'd spent the same amount of time slinging milkshakes at the local SBUX, but I'm more satisfied about the social good done by giving away PDFs and hosting a web site than I am about most anything else done in my career. (Perhaps <a href="http://search.cpan.org/perldoc?Test::Builder">Test::Builder</a> has been more influential from a code standpoint, but once I start going down that line of thought, I feel a complex insert-lengthy-German-compound-philosophical-term-here emotion about those large projects I worked on which seemed influential and important at the time and haven't actually gone anywhere.)</p> <p>At worst, <em>Modern Perl</em> gave Perl as practiced by its best programmers in the 2010s a concrete name. At best, it helped existing Perl programmers and hopefully some new ones avoid some of the worst parts and adopt some of the best parts.</p> <h2>Golden Handcuffs For a Very Modern Perl Consultant</h2> <p>With a brief foray into running my own non-publishing businesses (hey, if you want to give this <a href="https://trendshare.org/how-to-invest">free investing articles for novices</a> a link somewhere, that'd be great) revealing that starting a business from nothing is very, very difficult.</p> <p>This, I think, is one reason why so many small consultancies are consultancies and never manage to turn their brilliant ideas into shipping products because it's too lucrative to chase consulting dollars and too difficult to invest in products which may not pay off. This isn't the first time I ran into this, either. If my first job after the dot com crash had succeeded in producing its product, it would have owned a lucrative vertical market. Unfortunately, consulting dollars ran out before it could finish a proof of concept to jumpstart presales. (Of course, the investors also wanted to rewrite the Perl backend and the Python/Wx client application in Java, in 2002, to run on point of sale systems for small businesses, in 2002.)</p> <p>When consulting is good, it can be lucrative and that lucre can be tempting. Consultants have to charge a rate far in excess of a normal hourly salary because there's overhead: you may have a lot of spare time between contracts, you don't get PTO or benefits you don't pay for yourself, and you have to negotiate contracts and pursue work which may or may not materialize.</p> <p>If you find a good client, you treat him or her like a combination of the Pope, the Queen, and the Dalai Lama, because a trustworthy client who pays you on time and doesn't argue over little details of the contract is better than gold.</p> <p>Yet suppose you budget for eight or nine months of steady work and luck into a contract that gives you twice that. Some people deal well with that boom and bust. Others don't. (For me, the mechanics of budgeting are easy but the psychology of riding the business cycle is difficult.) It's difficult to look at other opportunities with an objective eye because when the contract is great, that money looks very nice and it represents a lot of freedom, but when the contracts are awful or far between, the stability of a salaried job represents a lot of security.</p> <h2>Finding a Perl Job</h2> <p>Am I a Perl programmer?</p> <p>If you look at my r茅sum茅, I've been fortunate (in one sense) to have spent the past 16 years working with Perl at a deep level on many, many projects in many jobs in many domains. I've also made money working with Python, SQL, shell, Ruby, C, C++, JavaScript, assembly, Java, SQL, PL/pgSQL, Visual Basic, PHP, LaTeX, and I'm sure I left out at least one or two.</p> <p>As a professional programmer with an interest in my professional development and someone who is physiologically unable <em>not</em> to program occasionally outside of 9-5, I've dabbled in other technologies—not enough to call myself an expert the way I call myself a Perl expert—but enough that I don't completely embarrass myself in conversation about them. (<a href="https://en.wikipedia.org/wiki/Charles_H._Moore">Chuck Moore</a> once tried to convince me of the value of writing my own operating system. I have an MP3 of this.)</p> <p>Yet on my CV, one technology keeps coming up again and again. <em>Perl</em></p> <p>It's no secret that that's where I've spent most of my time. Look, I <em>like</em> the language. I'm used to its flaws (even though a couple of hundred entries on this site will reveal that I don't like all of them) and I know how to play to its strengths. I like its community (people like Tim Bunce and Rik Signes and Andreas Koenig and Tatsuhiko Miyagawa are, frankly, irreplaceable elsewhere). I'm exceedingly productive with Perl in a way that would take me a while to get productive in other languages. That's what you get from a decade and a half of experience. That's what you get from at least 20,000 hours of practice.</p> <h2>But Did You Learn to <em>Program</em>?</h2> <p>You tell me. I can give you references who will speak of me in positive terms. I can refer to to people who will tell you I helped solve problems or helped teach them something or helped them think about their problems in better ways.</p> <p>(You'll also find people who will say I can be sarcastic and moody and bitter, and for better or worse you're reading this, so why pretend?)</p> <p><em>This</em> is the important question that a CV can't answer. This is an important question that rifling through the small subset of my professional work that's on Github or the CPAN won't tell you. They won't tell you about all of the mistakes I made that you don't see, if they exist. They won't tell you about all of the thrashing around that I did or didn't do which Git let me squash out of existence. They won't tell you if I churned out 1400 lines of code one afternoon in a marathon coding session and it compiled on my second try and hasn't been touched since, because it just works and no one <em>needs</em> to touch it.</p> <p>Then again, asking me to write a sorting algorithm on a whiteboard won't tell you much either beyond "Does this person actually seem like he knows how to program <em>at all</em>?" and <em>that's</em> still a thing in interviews.</p> <p>You can't look at a CV and tell whether I'm the kind of person who will go above and beyond the task of transcribing what a software architect dictates that the project manager has teased out of what the director of sales has forced the enterprise sales representative to admit that he promised to the customer for the next release. (I <em>can</em> tell you that I would find that environment frustrating.) You can't predict only from a page and a half whether I have an enthusiasm for writing the best darn medical insurance billing code ever or whether that enthusiasm will translate into an attempt to delight users even if they never think about all of the bugs and frustrations that we squashed and eliminated before they escaped to production.</p> <p>You can't tell that by reading <em>Perl</em> or <em>Ruby</em> or <em>PostgreSQL</em> on my r茅sum茅. You can't even tell that by reading my code. That's the risk you take in hiring someone, but isn't that at least as important as answering the question "Can you make a decent attempt to write something resembling code on a whiteboard, even though your muscle memory has been tuned over several years for Vim, because you're ostensibly a <em>professional</em> who cares about tools and automation and making it easy to do the easy work of typing?" or the question "How well do you fit with our team?"</p> <h2>But Where are the <em>Perl</em> Jobs?</h2> <p>I'm not a unique and special snowflake. Programmers <em>aren't</em> fungible, but there aren't so many difficult programming tasks that you need the world's singular expert in a subject. (The fact that my degree says Music and not "Computer Science" from Stanford, Berkeley, MIT, or CMU probably keeps me out of computer vision, AI research, and compiler design, however, just as much as it's garlic to the Google recruiters who want me to believe it's an honor to spend weeks answering questions about ping pong balls and sorting phone books for the opportunity to babysit a data center full of machines dedicated to slapping ads on the world's information.)</p> <p>It should be no secret that, if I stay in programming, my preference is to work with the language of my expertise and preference. I believe that <a href="https://outspeaking.com/words-of-technology/the-lemon-market-of-programming-language-adoption.html">the fashion-driven chasing of programming fads is bad for business and programmer alike</a>, but that world isn't changing any time soon.</p> <p>As I see it, you can find a Perl job in one of a few ways:</p> <ul> <li>Create it yourself by starting your own company</li> <li>Create it yourself by recruiting a client without a strong technology preference</li> <li>Create it yourself by introducing Perl slowly into an existing job and making it irreplaceable</li> <li>Find one of the existing companies using Perl (read "Move to Amsterdam" or "spend time maintaining a codebase from 1997" or "get very lucky") <li>Create something so amazing and compelling and useful that people can't <em>not</em> use it, like mod_perl or Movable Type or, let's throw cPanel and Catalyst in there</li> <li>...</li> </ul> <p>Fifteen or fourteen or even ten years ago, I might have railed against the inherent unfairness of a cruel and uncaring universe of technology driven by fashion, where technical superiority is less important than buzz, but I've tried these approaches over my career. As I ponder the midpoint of my career (asking myself "Should I get a CS degree?" or "Wouldn't it be more effective to get a degree in finance and manage a boutique legal firm?"—<em>I am not making this up</em>, but then again I thought about buying a bakery a couple of years ago and it took <a href="https://twitter.com/rbuels">Robert Buels</a> only a few minutes to talk me out of it), the broader question of patterns and trends comes up.</p> <h2>Is Any Technology Immune to This?</h2> <p>Apart from COBOL? Mumps? No.</p> <p>Do I want to program in Cobol or Mumps for the rest of my life, or even the rest of the afternoon? No.</p> <p>Look, I'm a programmer. I solve problems. I've managed teams and I've been managed. I've written documentation and I've written tests. Some days I've deleted more code than I've written and I've been glad of it. Some days I've solved customer problems by <em>not</em> writing code. Some days I've been elbow deep in profiles or Valgrind reports and other days the best thing I've done was add an index to a column in a database and it was totally worth all of the research to reach the point where that was the solution.</p> <p>I have my preferences in technology, sure. I also have my experiences. When you hire a programmer, you're getting his or her preferences <em>and</em> experiences and biases.</p> <p>Maybe I'm being too picky. Maybe it's natural to reach the mid-career crisis and say "Arguing over which syntax is best on the Internet is a poor use of time I could spend walking a dog or playing pinball with my eleven year old nephew or celebrating a housewarming with friends". Maybe it's acceptable to by cynical and say "I'm a professional, which means that even if I have no <em>personal</em> interest in managing clinical research records, you are paying me enough to bring my knowledge and experience and hard-won judgment to bear to solve this problem in an effective and trustworthy way", but I'm probably not going to spend my nights and weekends thinking about it.</p> <p>Alternately, perhaps I <em>should</em> have bought that bakery. Sure, you have to hire people you can trust and pay them a lot of your revenue and you have to get up at 4 am every day and health inspectors and land zoning and the prices of dairy products...</p> <p>... but you're not dealing with VCs who underpay you and dangle ever more diluted RSUs in front of you for that 1% chance of an acquihire payout that'll slightly pay more than if they'd paid you a market rate for the start, or clients who want the world in a week for $20 an hour because some teenager in Albania only charges $8 an hour but at least you speak English, or an HR department that sees "Perl" sprinkled liberally through your r茅sum茅 and thinks either you're the system administrator you started as in 1998 or someone who fell through a time warp in 1999, and by the way, they have the Internet on computers now, har har, JavaScript is a real language, would you kindly join us in the new spring lineup for 2014?</p> <h2>The Real Question</h2> <p>For someone whose life obligations preclude selling everything and living on a beach in Thailand or Belize for a year, how <em>do</em> you evaluate a career reaching its midpoint? How do you keep programming fresh? How do you market yourself as someone who <em>solves problems</em> instead of someone who transcribes ideas in the language du jour? Or do you leave programming altogether?</p> <p>I don't have the answers, but if there's anything a decade and a half of experience has given me, it's the ability even to <em>ask</em> those questions.</p> </div> </div> <div class="asset-footer"></div> </div> <div id="entry-575" class="entry-asset asset hentry"> <div class="asset-header"> <h2 class="asset-name entry-title"><a href='/mt/2014/02/managing-sqitch-with-make' rel='bookmark'>Managing Sqitch with Make</a></h2> <div class="asset-meta"> <span class="byline"> By <span class="vcard author"><a class="fn url" href="http://www.wgz.org/~chromatic">chromatic</a></span> on <abbr class="published" title="2014-02-09T06:00:01-08:00">February 9, 2014 6:00 AM</abbr> </span> </div> </div> <div class="asset-content entry-content"> <div class="asset-body"> <p>If you, like me, have worked on any project in rapid development, you've had the joy of dealing with changes to your database. Tables get added. Tables get removed. Columns get renamed. Constraints get added. Stored procedures change their parameters. Refactoring code is easy (if you have discipline, source code control, and an effective testing strategy), but refactoring your database seems more difficult.</p> <p>In the good old days, you may have had a directory full of little <em>.sql</em> files with DDL for schema changes and insert, add, delete, and update statements to mangle data.</p> <p><a href="http://sqitch.org/">Sqitch</a> is a database change management system designed and developed by the inestimable David Wheeler. David is also responsible for <a href="http://pgtap.org/">pgTAP</a>, which provides usable automated testing of PostgreSQL databases.</p> <p>Sqitch gives you a command-line tool to manage database changes. You can add new changesets, apply them, and even verify them. (In practice, verification happens automatically; Sqitch encourages you to write pgTAP-style tests to verify that your changes do the right thing. If your tests fail, Sqitch rolls back the change. Obviously this works best on mature database systems such as PostgreSQL.)</p> <p>I've used <a href="http://search.cpan.org/perldoc?DBIx::Class::DeploymentHandler">DBIx::Class::DeploymentHandler</a> on other projects successfully, but there was always a little mismatch between how I prefer to work (maintaining DDL by hand) and how <a href="http://search.cpan.org/perldoc?DBIx::Class">DBIC</a> tends to want you to work (manage your schema as a set of DBIC classes and let it generate the DDL for you). That's mostly personal preference, but I do believe the Sqitch documentation is easier to understand at the moment as well.</p> <p>The only substantive complaint of any sort I have with Sqitch at all is that its idea of an ideal directory structure in which to store migration files is different from my idea. (This may not be true of the most recent versions.) You can override its notions with environment variables, but I use it on multiple projects.</p> <p>A while back, on a previous project, <a href="http://allisonrandal.com/">Allison</a> said something like "What if we had a <em>Makefile</em> just to automate away the common commands that we're all typing all the time?" These commands did things like "Run a development version of the web server" or "Recreate the testing database" or "Update all of the dependencies we're using with <a href="http://search.cpan.org/perldoc?Carton">Carton</a>." While <a href="http://schwern.dreamhosters.com/talks/MakeMaker_Is_DOOMED/">I hate the use of Make as a requirement to build CPAN modules</a>, Make solved a real problem for us.</p> <p>The problem is: how do you create a consistent set of commands, scoped to the project, which collect common operations and configure external tools to work as the project desires? We had a little directory, <em>tools/</em>, because the kind of people who work on Unix-like systems and have no problem writing Perl code are the kind of people who have directories full of useful, single-purpose tools. Yet we wanted something a little different.</p> <p>Makefiles are unholy unions of dependency-based procedural programming and shell commands. They're not really either one, but at least they let you extend them so that you can type <code>make <em>command</em></code> and have something reasonable happen. You're not cluttering up your shell alias list with aliases for every project you have in development and you don't have to remember paths or invocations special to every project. You get a little bit of consistency so your brain can spend its power on more important differences.</p> <p>Here's what I have for the current project:</p> <pre><code><span class="Identifier">PSQL</span>=psql -h localhost -U mpuser <span class="Identifier">PG_LATEST_DDL</span>=db/current/myproj_ddl.sql <span class="Identifier">PG_TEST_DATA</span>=tools/test_db_build/test_db_fixtures.sql <span class="Identifier">SQITCH_CONFIG</span>=config/sqitch/sqitch.conf <span class="Identifier">HAS_TEST_ENVIRONMENT</span>:=<span class="Identifier">$(</span><span class="Statement">shell</span><span class="Identifier"> perl -Ilib -MMyProj::Config -e </span><span class="Constant">'print MyProj::Config->new->allow_testing_db'</span><span class="Identifier">)</span> <span class="Identifier">sqitch_cttester:</span> <span class="Special"> @</span><span class="Constant">echo sqitch deploy cttester</span> <span class="Comment"># Makefiles are tricky about indentation</span> <span class="PreProc">ifeq</span> (<span class="Constant">"</span><span class="Identifier">$(HAS_TEST_ENVIRONMENT)</span><span class="Constant">"</span>, <span class="Constant">"1"</span>) <span class="Special"> @</span>SQITCH_CONFIG=<span class="Identifier">${SQITCH_CONFIG}</span> sqitch -d cttester -u cttester deploy; true <span class="Special"> @</span>SQITCH_CONFIG=<span class="Identifier">${SQITCH_CONFIG}</span> sqitch -d cttester -u cttester status >/dev/null 2>&1 <span class="PreProc">else</span> <span class="Special"> @</span>echo not in test environment, skipping <span class="PreProc">endif</span> <span class="Identifier">sqitch_myproj:</span> <span class="Special"> @</span><span class="Constant">echo sqitch deploy myproj</span> <span class="Special"> @</span><span class="Constant">SQITCH_CONFIG=</span><span class="Identifier">${SQITCH_CONFIG}</span><span class="Constant"> sqitch deploy; true</span> <span class="Special"> @</span><span class="Constant">SQITCH_CONFIG=</span><span class="Identifier">${SQITCH_CONFIG}</span><span class="Constant"> sqitch status >/dev/null 2>&1</span> <span class="Identifier">sqitch_deploy:</span> sqitch_myproj <span class="Identifier">sqitch_add:</span> <span class="Constant"> SQITCH_CONFIG=</span><span class="Identifier">${SQITCH_CONFIG}</span><span class="Constant"> sqitch add </span><span class="Identifier">$(name)</span> <span class="Constant"> vim </span><span class="Constant">"config/sqitch/deploy/</span><span class="Identifier">$(name)</span><span class="Constant">.sql"</span><span class="Constant"> </span><span class="Constant">"config/sqitch/revert/</span><span class="Identifier">$(name)</span><span class="Constant">.sql"</span><span class="Constant"> </span><span class="Constant">"config/sqitch/verify/</span><span class="Identifier">$(name)</span><span class="Constant">.sql"</span> <span class="Constant"> git add </span><span class="Constant">"config/sqitch/deploy/</span><span class="Identifier">$(name)</span><span class="Constant">.sql"</span><span class="Constant"> </span><span class="Constant">"config/sqitch/revert/</span><span class="Identifier">$(name)</span><span class="Constant">.sql"</span><span class="Constant"> </span><span class="Constant">"config/sqitch/verify/</span><span class="Identifier">$(name)</span><span class="Constant">.sql"</span></code></pre> <p>First, the Makefile defines a couple of environment variables used throughout the file. The most important are <code>SQITCH_CONFIG</code>, which controls the location of the project-specific Sqitch configuration file and <code>HAS_TEST_ENVIRONMENT</code> which returns a boolean representing whether the current environment allows the use of a separate testing database. (The production server has no testing database. Development servers do.)</p> <p>I only ever use the <code>sqitch_deploy</code> and <code>sqitch_add</code> commands. The deployment command tells Sqitch to deploy the most recent migrations it knows about in its plan. Sqitch's <code>status</code> command returns a boolean representing the success or failure (so that the exit code of <code>make sqitch_deploy</code> is useful and that <code>make</code> command doesn't think that the target failed).</p> <p>The <code>sqitch_add</code> command helps add a new database migration. It takes one argument, so invoke it with <code>make sqitch_add name=<em>new_migration_name</em></code>. Not only does it add the migration, it opens the new files Sqitch has created in <code>vim</code> (it should use the <code>$EDITOR</code> environment variable, but I haven't needed to care about anyone other than Vim users yet) and then adds all of the saved files to git's index.</p> <p>This saves me a few dozen keystrokes and a few seconds every time I make a database change. If that sounds trivial to you, good. A few keystrokes and a few seconds <em>are</em> trivial. My brainpower <em>isn't</em> trivial. Those keystrokes and seconds mean the difference between staying in the zone and fumbling around trying to remember commands I don't use all day every day. They save me minutes every time I use them, if you count the friction of switching between "How do I do this in Sqitch again? What's the directory layout here?" and "What was I really working on?"</p> <p>Automating silly tasks which take up a few seconds here and there is valuable if you can get rid of <em>that</em> friction. Sqitch has helped remove that friction from database migrations. Automating Sqitch with a simple <em>Makefile</em> has gone even further.</p> </div> </div> <div class="asset-footer"></div> </div> <div id="entry-574" class="entry-asset asset hentry"> <div class="asset-header"> <h2 class="asset-name entry-title"><a href='/mt/2014/01/fatal-warnings-are-a-ticking-time-bomb' rel='bookmark'>Fatal Warnings are a Ticking Time Bomb</a></h2> <div class="asset-meta"> <span class="byline"> By <span class="vcard author"><a class="fn url" href="http://www.wgz.org/~chromatic">chromatic</a></span> on <abbr class="published" title="2014-01-29T06:00:01-08:00">January 29, 2014 6:00 AM</abbr> </span> </div> </div> <div class="asset-content entry-content"> <div class="asset-body"> <p>When Perl 5.6.0 introduced <a href="http://perldoc.perl.org/perllexwarn.html">lexical warnings</a>, it gave programmers finer control over what parts of their program would produce warnings and how to handle those warnings. The most important feature is that the <em>warnings</em> pragma has a lexical effect. In other words, rather than enabling warnings for the entire program unilaterally, some files and blocks can request stricter or looser warning categories than others.</p> <h2><strong>Whole-Program Robustness is Not a Library Concern</strong></h2> <p>This is especially important for Perl <em>applications</em> which are often a combination of custom code and CPAN modules. Even though the CPAN is free and open source software and even though many of the newer CPAN distributions have public source control repositories to which you can submit patches and feature requests, there's often a thick line between "code we maintain for this project" and "code someone else maintains". Lexical warnings mean that the maintainers of CPAN distributions can choose their own warning strategies and your team can choose your own warning strategy and those strategies don't have to be the same.</p> <p>Keep that separation of concerns in mind for a moment.</p> <h2><strong>Blurring the Lines Between Warnings and Exceptions</strong></h2> <p>The <em>warnings</em> pragma also included a feature which can promote any warnings caused in a lexical scope into exceptions. If, for the purpose of security or clarity or maintainability or coding standards, your team decides that uninitialized value warnings are so severe that they should be exceptional conditions, you can ask the <em>warnings</em> pragma to promote that warning to an exception within a lexical scope. This is a powerful feature that you should use with caution; unless you're prepared to catch those exceptions and deal with them (or not catch them and deal with the consequences), those exceptions will cause your program to terminate. That <em>may</em> be what you want, but if you've asked for it explicitly, Perl believes you know what you're doing.</p> <p>Perl also gives you the option to promote <em>all</em> warnings in <em>every</em> warning category to an exception with <code>use warnings FATAL => 'all';</code>. If enabling fatal warnings for one warning category is serious, promoting all warnings to exceptions is grave.</p> <h2><strong>Promoting Library Warnings to Exceptions</strong></h2> <p>The <em>warnings</em> pragma also added an interesting feature by which modules can register their own categories of warnings. Perl will treat them as lexical warnings just as if they were core warnings. In other words, a module which chooses to register its own warning category will be able to emit warnings which respect a <code>use warnings;</code> or <code>no warnings;</code> in caller scopes.</p> <p>This is a wonderful feature because it respects the separation of concerns. It's not up to a library to dictate what the users of that library consider warnable conditions. The users of that library can elect to accept warnings or disable them as they see fit.</p> <p>Warnings registered with <em>warnings</em> also fall under the purview of the fatal warnings promotion code. If you've enabled fatal warnings and if any of the modules you use within that lexical scope emit a warning, that module's warning will become an exception.</p> <h2><strong>Fatal Warnings are Not Future Proof</strong></h2> <p>If you have the mindset for reading awkward bug reports, <a href="https://rt.perl.org/Ticket/Display.html?id=121085">Perl RT #121085</a> demonstrates one serious danger of unilateral fatal warnings. Because you've asked Perl to treat <em>all</em> warnings as fatal, any warning you get will be fatal.</p> <p>Because Perl, by default, does not treat warnings as fatal (because Perl uses the word "exception" to mean "exception" and "warning" to mean "warning", thus distinguishing between the two as a matter of syntax <em>and</em> semantics), it's possible that a newer major release of Perl may emit more warnings than a older major release of Perl. In fact, that happens. As Perl evolves and improves and Perl users discover more interesting and useful interactions with the language and as bugs get fixed, certain conditions lend themselves to error messages.</p> <p>If you've written <code>use warnings FATAL => 'all';</code>, you've accepted the responsibility for checking to see if a newer release of Perl emits newer warnings. Even if you didn't know you had that responsibility, you should have known. That's what you asked for. Your program threw an exception and you didn't catch it. That's your responsibility.</p> <p>Many people know that. That fact is rarely controversial. If you read the RT #121085 bug report, you'll see the argument that p5p should be extra careful adding new warnings because some people choose to treat them as exceptions, and so they're effectively exceptions, and p5p is causing previously valid programs to break—but that's a silly argument.</p> <p>Perl developers brag rightly about Perl's commitment to backwards compatibility, but an understated corollary to backwards compatibility is forwards compatibility. It's your responsibility as a developer to write code that's compatible with future releases of Perl (within reason) or to face the consequences. If you write code that is <em>likely</em> to break when upgrading to a new version of Perl, that's your fault. p5p will do its best to ease the transition (that's why deprecations are multi-year processes which begin with <em>warnings</em> and eventually become exceptions), but you have a responsibility too.</p> <p>That fact is a little bit more controversial, but it's pretty well established on p5p. It's not at all well established on the CPAN, which is where the problem gets a lot more serious and subtle.</p> <p>Remember how modules can register their own lexical warnings? Just as Perl may add new warnings in major releases, modules can add new warnings—except that CPAN modules can add new warnings <em>whenever they see fit</em>, because there are no normative community standards about when it's acceptable to add new warnings, how often releases will happen, how long the support period for releases is, and any commitment to backwards compatibility or deprecation.</p> <p>If you've enabled fatal warnings, you've asked for any warnings in any CPAN module that you didn't write and you don't maintain to become exceptions which may terminate your program. The risk isn't "We upgrade our core Perl once a year and read the list of changes then run our entire test suite and only then deploy a new version". The risk is "We've updated CPAN modules and now have to audit all of them for new warnings."</p> <p>That's a bigger risk—but it gets worse.</p> <h2><strong>Fatal Warnings Do Not Belong in Module Code</strong></h2> <p>What if a CPAN module you use but do not maintain uses fatal warnings? Any change to its dependencies which throws a warning the module did not account for may now throw an exception. Because it's in code you do not maintain, it's time to break out the debugger to figure out what went wrong, then fire off a patch and hope the maintainer releases a new version. In the meantime, your code is broken because a CPAN maintainer did not think about the risk of promoting warnings to exceptions for circumstances which are outside of his or her control.</p> <p>"Wait," you say. "Which CPAN author would be silly enough to write <code>use warnings FATAL => 'all';</code> in library code? That's awful, and I wouldn't use that code."</p> <p>The problem is that <a href="http://search.cpan.org/perldoc?Moo">Moo</a> enables <a href="http://search.cpan.org/perldoc?strictures">strictures</a> by default in all code that simply writes <code>use Moo;</code> and <code>strictures</code> enables fatal warnings in all code which uses <code>strictures</code>, so writing <code>use Moo;</code> enables fatal warnings in the enclosing lexical scope.</p> <p>In other words, any CPAN module which uses <code>Moo</code> is, by default, vulnerable to a change in any of its dependencies which may legitimately produce new warnings. By "vulnerable" I mean "your program may start crashing in library code you didn't write and do not maintain and, by the way, may be in a dependency several levels deep that you didn't even know uses <code>Moo</code>."</p> <p>One solution is to use <a href="http://search.cpan.org/perldoc?Moo::Lax">Moo::Lax</a> instead of <code>Moo</code> in code you write for the CPAN. That doesn't fix existing code, but it doesn't make the problem worse.</p> <p>I suppose the well-intentioned people who wrote <code>strictures</code> could make the argument that "You should be submitting patches against CPAN modules anyhow!", but I have trouble reconciling that against their justification for enabling fatal warnings, which seems to be "Novices should take exceptions seriously!" as the argument then becomes "If new Perl programmers aren't willing to learn enough about Perl to submit upstream patches when our code crashes their programs, there are many other languages they can use with library ecosystems which aren't hostile to new developers"—and that's a silly argument to make too. (Then again, <code>strictures</code> has the misfeature, documented in <a href="https://rt.cpan.org/Ticket/Display.html?id=79504">RT #79504</a>, that its behavior will change if you're in or slightly under a directory managed by a VCS <em>even if you're not a Perl developer</em> or <em>even if it's not a Perl project</em> or <em>even if the program you're running has nothing to do with the directory</em> or <em>even if <code>strictures</code> is a dependency of library code several levels deep</em>.)</p> <h2><strong>In Other Words, <code>strictures</code> is Broken?</strong></h2> <p>I suppose you could claim that it's <code>Moo</code> that's broken, for inflicting the silliness that is the current behavior of <code>strictures</code> on the CPAN and all downstream code that's unfortunate enough to somehow, somewhere use code that depends on <code>Moo</code>, but the <em>real</em> problem is that <code>use warnings FATAL => 'all';</code> does not belong in <em>library</em> code that you're handing other people to use. It's just too risky.</p> </div> </div> <div class="asset-footer"></div> </div> <div class="content-nav"> <a href='/mt/archives'>Archives</a> </div> </div> </div> <div id="beta"> <div id="beta-inner"> <div class="widget-mp-thebook widget"> <h3 class="widget-header">Modern Perl: The Book</h3> <div class="widget-content"> <a href="http://www.amazon.com/gp/product/1680500880?ie=UTF8&tag=mp1234-20"><img src="/images/mp_2016_cover_thumb.jpg" style="border: 1px solid; text-align: center;" alt="cover image for Modern Perl: the book" /></a><br /> <p>The best Perl Programmers read <a href="http://modernperlbooks.com/books/modern_perl_2014/">Modern Perl: The Book</a>.</p> <p><small>sponsored by the <a href="https://blenderrecipereviews.com/recipes/smoothies/how-to-make-a-smoothie">How to Make a Smoothie</a> guide</small></p> </div> </div> <div class="widget-recent-entries widget-archives widget"> <h3 class="widget-header">Recent Entries</h3> <div class="widget-content"> <ul> <li><a href='/mt/2014/05/modern-perl-2014-electronic-editions-released'>Modern Perl: 2014 Electronic Editions Released</a></li> <li><a href='/mt/2014/04/modern-perl-2014-edition-is-out'>Modern Perl: 2014 Edition is Out</a></li> <li><a href='/mt/2014/02/the-mid-career-crisis-of-the-perl-programmer'>The Mid-Career Crisis of the Perl Programmer</a></li> <li><a href='/mt/2014/02/managing-sqitch-with-make'>Managing Sqitch with Make</a></li> <li><a href='/mt/2014/01/fatal-warnings-are-a-ticking-time-bomb'>Fatal Warnings are a Ticking Time Bomb</a></li> <li><a href='/mt/2014/01/perls-special-named-code-blocks'>Perl's Special Named Code Blocks</a></li> <li><a href='/mt/2014/01/how-to-rewrite-software-without-destroying-your-business'>How to Rewrite Software without Destroying Your Business</a></li> <li><a href='/mt/2014/01/secrets-of-cpanm-cpanfile-support'>Secrets of cpanm cpanfile Support</a></li> <li><a href='/mt/2014/01/the-limits-of-a-programming-language-vision'>The Limits of a Programming Language Vision</a></li> <li><a href='/mt/2013/12/profile-your-tests-one-test-class-per-file'>Profile Your Tests (One Test Class Per File)</a></li> </ul> </div> </div> <div class="widget-recent-comments widget"> <h3 class="widget-header">Recent Comments</h3> <div class="widget-content"> <ul> <li><strong>chromatic:</strong> I haven't used it for anything serious, so I don't <a href='/mt/2013/06/continuations-and-the-web#comment-779140' title='full comment on: Continuations and the Web'>read more</a></li> <li><strong>Sam H.:</strong> How do you feel about Continuity? <a href='/mt/2013/06/continuations-and-the-web#comment-768357' title='full comment on: Continuations and the Web'>read more</a></li> <li><strong>chromatic:</strong> I agree that that's no fun, but p5p's not willing <a href='/mt/2013/05/ejecting-cgipm-from-the-perl-core#comment-383568' title='full comment on: Ejecting CGI.pm From the Perl Core'>read more</a></li> <li><strong>David Pottage:</strong> Who will upgrade to perl 5.20 without installing CGI from <a href='/mt/2013/05/ejecting-cgipm-from-the-perl-core#comment-383460' title='full comment on: Ejecting CGI.pm From the Perl Core'>read more</a></li> <li><strong>chromatic:</strong> Without a small army of new volunteers appearing to do <a href='/mt/2013/05/ejecting-cgipm-from-the-perl-core#comment-344043' title='full comment on: Ejecting CGI.pm From the Perl Core'>read more</a></li> <li><strong>chromatic:</strong> I meant among the participants in the p5p discussion. <a href='/mt/2013/05/ejecting-cgipm-from-the-perl-core#comment-343977' title='full comment on: Ejecting CGI.pm From the Perl Core'>read more</a></li> <li><strong>chromatic:</strong> How will it cause problems? Who's upgrading to Perl 5.20 <a href='/mt/2013/05/ejecting-cgipm-from-the-perl-core#comment-343939' title='full comment on: Ejecting CGI.pm From the Perl Core'>read more</a></li> <li><strong>LpSolit:</strong> It's wrong to say that no one uses CGI.pm. Bugzilla <a href='/mt/2013/05/ejecting-cgipm-from-the-perl-core#comment-343871' title='full comment on: Ejecting CGI.pm From the Perl Core'>read more</a></li> <li><strong>Andrew DeFaria:</strong> If you really think that "nobody uses CGI.pm" then you <a href='/mt/2013/05/ejecting-cgipm-from-the-perl-core#comment-343208' title='full comment on: Ejecting CGI.pm From the Perl Core'>read more</a></li> <li><strong>David Pottage:</strong> If we remove CGI, then we should replace it with <a href='/mt/2013/05/ejecting-cgipm-from-the-perl-core#comment-341303' title='full comment on: Ejecting CGI.pm From the Perl Core'>read more</a></li> </ul> </div> </div> <div class="widget-tag-cloud widget"> <h3 class="widget-header">Tag Cloud</h3> <div class="widget-content"> <ul> <li class="rank-6"><a href="javascript:void(0)" onclick="location.href='http://www.modernperlbooks.com/cgi-bin/mt/mt-search.cgi?blog_id=1&tag=advocacy&limit=20';return false;" rel="tag">advocacy</a></li> <li class="rank-6"><a href="javascript:void(0)" onclick="location.href='http://www.modernperlbooks.com/cgi-bin/mt/mt-search.cgi?blog_id=1&tag=books&limit=20';return false;" rel="tag">books</a></li> <li class="rank-6"><a href="javascript:void(0)" onclick="location.href='http://www.modernperlbooks.com/cgi-bin/mt/mt-search.cgi?blog_id=1&tag=community&limit=20';return false;" rel="tag">community</a></li> <li class="rank-4"><a href="javascript:void(0)" onclick="location.href='http://www.modernperlbooks.com/cgi-bin/mt/mt-search.cgi?blog_id=1&tag=CPAN&limit=20';return false;" rel="tag">CPAN</a></li> <li class="rank-5"><a href="javascript:void(0)" onclick="location.href='http://www.modernperlbooks.com/cgi-bin/mt/mt-search.cgi?blog_id=1&tag=cpan&limit=20';return false;" rel="tag">cpan</a></li> <li class="rank-5"><a href="javascript:void(0)" onclick="location.href='http://www.modernperlbooks.com/cgi-bin/mt/mt-search.cgi?blog_id=1&tag=design&limit=20';return false;" rel="tag">design</a></li> <li class="rank-4"><a href="javascript:void(0)" onclick="location.href='http://www.modernperlbooks.com/cgi-bin/mt/mt-search.cgi?blog_id=1&tag=language%20design&limit=20';return false;" rel="tag">language design</a></li> <li class="rank-6"><a href="javascript:void(0)" onclick="location.href='http://www.modernperlbooks.com/cgi-bin/mt/mt-search.cgi?blog_id=1&tag=maintainability&limit=20';return false;" rel="tag">maintainability</a></li> <li class="rank-6"><a href="javascript:void(0)" onclick="location.href='http://www.modernperlbooks.com/cgi-bin/mt/mt-search.cgi?blog_id=1&tag=marketing&limit=20';return false;" rel="tag">marketing</a></li> <li class="rank-1"><a href="javascript:void(0)" onclick="location.href='http://www.modernperlbooks.com/cgi-bin/mt/mt-search.cgi?blog_id=1&tag=modern%20perl&limit=20';return false;" rel="tag">modern perl</a></li> <li class="rank-5"><a href="javascript:void(0)" onclick="location.href='http://www.modernperlbooks.com/cgi-bin/mt/mt-search.cgi?blog_id=1&tag=moose&limit=20';return false;" rel="tag">moose</a></li> <li class="rank-5"><a href="javascript:void(0)" onclick="location.href='http://www.modernperlbooks.com/cgi-bin/mt/mt-search.cgi?blog_id=1&tag=novices&limit=20';return false;" rel="tag">novices</a></li> <li class="rank-5"><a href="javascript:void(0)" onclick="location.href='http://www.modernperlbooks.com/cgi-bin/mt/mt-search.cgi?blog_id=1&tag=parrot&limit=20';return false;" rel="tag">parrot</a></li> <li class="rank-2"><a href="javascript:void(0)" onclick="location.href='http://www.modernperlbooks.com/cgi-bin/mt/mt-search.cgi?blog_id=1&tag=perl&limit=20';return false;" rel="tag">perl</a></li> <li class="rank-2"><a href="javascript:void(0)" onclick="location.href='http://www.modernperlbooks.com/cgi-bin/mt/mt-search.cgi?blog_id=1&tag=perl%205&limit=20';return false;" rel="tag">perl 5</a></li> <li class="rank-4"><a href="javascript:void(0)" onclick="location.href='http://www.modernperlbooks.com/cgi-bin/mt/mt-search.cgi?blog_id=1&tag=perl%206&limit=20';return false;" rel="tag">perl 6</a></li> <li class="rank-4"><a href="javascript:void(0)" onclick="location.href='http://www.modernperlbooks.com/cgi-bin/mt/mt-search.cgi?blog_id=1&tag=perl%20programming&limit=20';return false;" rel="tag">perl programming</a></li> <li class="rank-4"><a href="javascript:void(0)" onclick="location.href='http://www.modernperlbooks.com/cgi-bin/mt/mt-search.cgi?blog_id=1&tag=software%20development&limit=20';return false;" rel="tag">software development</a></li> <li class="rank-4"><a href="javascript:void(0)" onclick="location.href='http://www.modernperlbooks.com/cgi-bin/mt/mt-search.cgi?blog_id=1&tag=testing&limit=20';return false;" rel="tag">testing</a></li> <li class="rank-6"><a href="javascript:void(0)" onclick="location.href='http://www.modernperlbooks.com/cgi-bin/mt/mt-search.cgi?blog_id=1&tag=web%20programming&limit=20';return false;" rel="tag">web programming</a></li> </ul> </div> </div> <div class="widget-archive widget-archive-category widget"> <h3 class="widget-header">Categories</h3> <div class="widget-content"> </div> </div> <div class="widget-archive-monthly widget-archive widget"> <h3 class="widget-header">Monthly <a href='/mt/archives'>Archives</a></h3> <div class="widget-content"> <ul> <li><a href="/mt/2014/05/">May 2014 (1)</a></li> <li><a href="/mt/2014/04/">April 2014 (1)</a></li> <li><a href="/mt/2014/02/">February 2014 (2)</a></li> <li><a href="/mt/2014/01/">January 2014 (5)</a></li> <li><a href="/mt/2013/12/">December 2013 (4)</a></li> <li><a href="/mt/2013/11/">November 2013 (4)</a></li> <li><a href="/mt/2013/10/">October 2013 (4)</a></li> <li><a href="/mt/2013/09/">September 2013 (3)</a></li> <li><a href="/mt/2013/08/">August 2013 (4)</a></li> <li><a href="/mt/2013/07/">July 2013 (4)</a></li> <li><a href="/mt/2013/06/">June 2013 (4)</a></li> <li><a href="/mt/2013/05/">May 2013 (4)</a></li> <li><a href="/mt/2013/04/">April 2013 (4)</a></li> <li><a href="/mt/2013/03/">March 2013 (4)</a></li> <li><a href="/mt/2013/02/">February 2013 (6)</a></li> <li><a href="/mt/2013/01/">January 2013 (4)</a></li> <li><a href="/mt/2012/12/">December 2012 (6)</a></li> <li><a href="/mt/2012/11/">November 2012 (6)</a></li> <li><a href="/mt/2012/10/">October 2012 (10)</a></li> <li><a href="/mt/2012/09/">September 2012 (11)</a></li> <li><a href="/mt/2012/08/">August 2012 (12)</a></li> <li><a href="/mt/2012/07/">July 2012 (10)</a></li> <li><a href="/mt/2012/06/">June 2012 (9)</a></li> <li><a href="/mt/2012/05/">May 2012 (12)</a></li> <li><a href="/mt/2012/04/">April 2012 (13)</a></li> <li><a href="/mt/2012/03/">March 2012 (11)</a></li> <li><a href="/mt/2012/02/">February 2012 (12)</a></li> <li><a href="/mt/2012/01/">January 2012 (10)</a></li> <li><a href="/mt/2011/12/">December 2011 (9)</a></li> <li><a href="/mt/2011/11/">November 2011 (13)</a></li> <li><a href="/mt/2011/10/">October 2011 (10)</a></li> <li><a href="/mt/2011/09/">September 2011 (12)</a></li> <li><a href="/mt/2011/08/">August 2011 (11)</a></li> <li><a href="/mt/2011/07/">July 2011 (10)</a></li> <li><a href="/mt/2011/06/">June 2011 (13)</a></li> <li><a href="/mt/2011/05/">May 2011 (12)</a></li> <li><a href="/mt/2011/04/">April 2011 (9)</a></li> <li><a href="/mt/2011/03/">March 2011 (10)</a></li> <li><a href="/mt/2011/02/">February 2011 (11)</a></li> <li><a href="/mt/2011/01/">January 2011 (12)</a></li> <li><a href="/mt/2010/12/">December 2010 (10)</a></li> <li><a href="/mt/2010/11/">November 2010 (10)</a></li> <li><a href="/mt/2010/10/">October 2010 (9)</a></li> <li><a href="/mt/2010/09/">September 2010 (12)</a></li> <li><a href="/mt/2010/08/">August 2010 (9)</a></li> <li><a href="/mt/2010/07/">July 2010 (12)</a></li> <li><a href="/mt/2010/06/">June 2010 (10)</a></li> <li><a href="/mt/2010/05/">May 2010 (10)</a></li> <li><a href="/mt/2010/04/">April 2010 (13)</a></li> <li><a href="/mt/2010/03/">March 2010 (12)</a></li> <li><a href="/mt/2010/02/">February 2010 (11)</a></li> <li><a href="/mt/2010/01/">January 2010 (9)</a></li> <li><a href="/mt/2009/12/">December 2009 (10)</a></li> <li><a href="/mt/2009/11/">November 2009 (11)</a></li> <li><a href="/mt/2009/10/">October 2009 (10)</a></li> <li><a href="/mt/2009/09/">September 2009 (13)</a></li> <li><a href="/mt/2009/08/">August 2009 (12)</a></li> <li><a href="/mt/2009/07/">July 2009 (14)</a></li> <li><a href="/mt/2009/06/">June 2009 (11)</a></li> <li><a href="/mt/2009/05/">May 2009 (10)</a></li> <li><a href="/mt/2009/04/">April 2009 (12)</a></li> <li><a href="/mt/2009/03/">March 2009 (12)</a></li> <li><a href="/mt/2009/02/">February 2009 (12)</a></li> <li><a href="/mt/2009/01/">January 2009 (4)</a></li> </ul> </div> </div> <div class="widget-pages widget"> <h3 class="widget-header">Pages</h3> <div class="widget-content"> <ul> </ul> </div> </div> <div class="widget-syndication widget"> <div class="widget-content"> <ul> <li><img src="/mt-static/images/status_icons/feed.gif" alt="Subscribe to feed" width="9" height="9" /> <a href="/mt/atom.xml">Subscribe to this blog's feed</a></li> </ul> </div> </div> <div class="widget-about-this-page widget"> <h3 class="widget-header"> </h3> <div class="widget-content"> <p>Find recent content on the <a href="/mt/">main index</a> or look in the <a href='/mt/archives'>archives</a> to find all content.</p> <hr /> <p>Powered by the <a href="http://perl.org/">Perl programming language</a></p> <p><a href="https://outspeaking.com/words-of-technology/what-is-programming.html">what is programming?</a></p> </div> </div> </div> </div> </div> </div> <div id="footer"> <div id="footer-inner"> <div id="footer-content"> <div class="widget-powered widget"> <div class="widget-content"> Powered by <a href="http://www.movabletype.com/" rel="generator">Movable Type</a> </div> </div> <div class="widget-creative-commons widget"> <div class="widget-content"> This blog is licensed under a <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/us/">Creative Commons License</a>. </div> </div> </div> </div> </div> <script type="text/javascript"> var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-7980025-2']); _gaq.push(['_trackPageview']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })(); </script> </div> </div> </body> </html>