Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Maintaining Code While Staying Sane Peter Scott O’Reilly School of Technology February 2011
Dealing With Legacy Perl <ul><li>Legacy Perl can stink </li></ul><ul><li>Even when you wrote it </li></ul><ul><li>Or espec...
Why So Many Ugly Perl Programs? <ul><li>Unfortunately, some of those ways stink </li></ul><ul><li>Or, people use more than...
The “DWIM” Myth <ul><li>“ Perl programming doesn’t require the same discipline as other languages” </li></ul><ul><li>Indee...
The “Prototyping Only” Myth <ul><li>“ Perl is too slow and/or unpredictable to be used for serious work” </li></ul><ul><li...
The “$@%*!” Myth <ul><li>“ Perl is a write-only language” </li></ul><ul><li>Another product of insufficient discipline </l...
Find the Author(s)! <ul><li>Are they a better  programmer than you  or worse? </li></ul><ul><li>Especially, better or  wor...
What Are You Dealing With? <ul><li>What was the code optimized for? </li></ul><ul><ul><li>Maintainability </li></ul></ul><...
Maintainability <ul><li># Print words with an even number of letters, AND even </li></ul><ul><li># number of each vowel, A...
Performance <ul><li>while (<>) </li></ul><ul><li>{  </li></ul><ul><li>next if ($. | length() - 1)) % 2;  </li></ul><ul><li...
Brevity <ul><li>#!/usr/bin/perl -ln </li></ul><ul><li>($x=aeiouy)=~s#.#y/$&//|#g;eval(&quot;$x$.|y///c&quot;)%2&&next;prin...
Job Security <ul><li>@i = map { chop; $x++ %2 ? $_ : () } <>; </li></ul><ul><li>while ($i = shift @i) </li></ul><ul><li>{ ...
Testing <ul><li>You can’t test too early </li></ul><ul><ul><li>Or too much </li></ul></ul><ul><ul><li>Use  Test::More </li...
Tests are Real Programs, Too <ul><li>Don’t abandon good indentation, variable naming, design, etc just because they’re “te...
Testing Web Applications <ul><li>Good design would mean you wouldn't have to go through a web server, of course </li></ul>...
Web Testing Example <ul><li>my $ua = WWW::Mechanize->new; </li></ul><ul><li>my $res = $ua->get(&quot;http://www.example.co...
Modern Web Testing <ul><li>Now you can use  Test::WWW::Mechanize </li></ul><ul><ul><li>$mech->get_ok(...) </li></ul></ul><...
Layout <ul><li>Code should be pretty  to look at </li></ul><ul><li>Add comments where  you had to think a lot </li></ul><u...
Before  perltidy <ul><li>for my $word (keys %{$word{$len}}){ </li></ul><ul><li>chop(my $prefix = $word);if ($opt{g}){  </l...
After  perltidy <ul><li>for my $word (keys %{$word{$len}}) {  </li></ul><ul><li>chop(my $prefix = $word);  </li></ul><ul><...
After  perltidy <ul><li>for my $word (keys %{$word{$len}})  </li></ul><ul><li>{  </li></ul><ul><li>chop(my $prefix = $word...
Analysis <ul><li>Eliminate superfluous code  through coverage analysis: </li></ul><ul><ul><li>Devel::Coverage </li></ul></...
Devel::NYTProf <ul><li>Very new </li></ul><ul><li>Incredibly flexible and accurate </li></ul><ul><li>Terrific reporting </...
Devel::NYTProf
What to Look Out For in Inherited Code <ul><li>Apparent level of Perl  expertise </li></ul><ul><ul><li>Uses hashes? Regexe...
The Documentation Hound <ul><li>########################################## </li></ul><ul><li># Function name: increment_nu...
The Documentation Hound <ul><li>########################################## </li></ul><ul><li># Function name: increment_nu...
The Documentation Hound Cure <ul><li>s/^#+n//mg </li></ul><ul><li>Move to POD later in this file </li></ul><ul><ul><li>Or ...
The Documentation Hound Cure <ul><li>For local programs, author and history information can be taken care of by a source c...
String Manipulation, BASIC-Style <ul><li>$repl = ' '; </li></ul><ul><li>for ($off = 0; $off < length($str); $off++) { </li...
…  i.e., Without Regexes <ul><li>$str =~ s/D+/ /g; </li></ul>
Nice Formatting, But… <ul><li>if ($month == &quot;1&quot;) { $month = &quot;0&quot; . $month; } </li></ul><ul><li>if ($mon...
Nice Formatting, But… <ul><li>$month = sprintf &quot;%02d&quot;, $month; </li></ul>
Too Much Time on Their Hands <ul><li>$smtp->datasend(&quot;To: santa.claus@north.polen&quot;); </li></ul><ul><li>$smtp->da...
Too Much Time on Their Hands <ul><li>$smtp->datasend(<<'EOTEXT'); </li></ul><ul><li>To: santa.claus@north.pole </li></ul><...
Too Much Time on Their Hands <ul><li>$smtp->datasend(<<'EOTEXT' =~ /[^Sn]*(.*?n)/g); </li></ul><ul><li>To: santa.claus@nor...
<ul><li>$smtp->datasend(<<'EOTEXT' =~ /[^Sn]*(.*?n)/g); </li></ul><ul><li>EOTEXT </li></ul>Too Much Time on Their Hands To...
Way Too Much Time On Their hands <ul><li>print &quot;<HTML><HEAD>n&quot;; </li></ul><ul><li>print &quot;<TITLE>My Home Pag...
<ul><li>my ($sec, $min, $hour, </li></ul><ul><li>$mday, $mon, $year, </li></ul><ul><li>$wday, $yday, $isdst) </li></ul><ul...
<ul><li>my ($mday, $mon) </li></ul><ul><li>=(localtime)[4,3]; </li></ul>The Perils of Cut and Paste
<ul><li>my ($mday, $mon) </li></ul><ul><li>= (localtime)[4,3]; </li></ul>The Perils of Cut and Paste
<ul><li>use Time::localtime; </li></ul><ul><li>my ($mday, $mon) </li></ul><ul><li>= (localtime->mday, localtime->mon); </l...
Scope? What is This Thing You Call Scope? <ul><li>my ($count, $ncount, @recs, @nrecs, %ccname, %ccphone, %ccaddr, %cccity)...
Scope Ignorance Cure <ul><li>Move variable declaration to latest possible point </li></ul><ul><ul><li>Exception: configura...
Monolithic Madness <ul><li>(Visualize 2500 lines of code without the word 'sub') </li></ul><ul><li>Sufferers’ favorite lan...
Monolithic Madness Cure <ul><li>Look for variables with short scopes and evaluate the area for subroutine-ness </li></ul><...
Perl from ??? <ul><li>$#abspaths = $num; </li></ul><ul><li>for ($i=0; $i<$num; $i++) { </li></ul><ul><li>my $newlen = </li...
Perl from C <ul><li>abspaths = realloc(abspaths,  </li></ul><ul><li>num * sizeof(char*)); </li></ul><ul><li>for ( i=0;  i<...
Perl from C <ul><li>@abspaths = map { &quot;$ROOT/$_&quot; } @paths; </li></ul>
Perl from ??? <ul><li>$file = &quot;matrix.dat&quot;; </li></ul><ul><li>open (FH,  &quot;>$file&quot;); </li></ul><ul><li>...
Perl from FORTRAN <ul><li>ofile = &quot;matrix.dat&quot; </li></ul><ul><li>OPEN (42, FILE=ofile) </li></ul><ul><li>DO 10 I...
Perl from FORTRAN <ul><li>open my $fh, '>', $file or die $!; </li></ul><ul><li>for my $i (1 .. 4) {  </li></ul><ul><li>pri...
Perl from ??? <ul><li>###################### </li></ul><ul><li>#Program name: Report. </li></ul><ul><li>##################...
Perl from COBOL <ul><li>IDENTIFICATION DIVISION. </li></ul><ul><li>PROGRAM-ID.  Report. </li></ul><ul><li>DATA DIVISION. <...
Perl from COBOL <ul><li>my $rec = <>; </li></ul><ul><li>my ($bonus, $base) = unpack &quot;A7x4A7&quot;, $rec; </li></ul><u...
Perl from ??? <ul><li>#!/usr/bin/perl -l </li></ul><ul><li>print &quot;Think of a number: &quot;; $dummy = <>; </li></ul><...
Perl from BASIC <ul><li>100  INPUT &quot;Think of a number: &quot;;D$ </li></ul><ul><li>110  LET I = 1 </li></ul><ul><li>1...
Perl from BASIC <ul><li>print &quot;Think of a numbern&quot;; </li></ul><ul><li>my $ans = ''; </li></ul><ul><li>for (my $g...
<ul><li>read(STDIN, $buffer, $ENV{CONTENT_LENGTH}); </li></ul><ul><li>my @pairs = split(/&/, $buffer);  </li></ul><ul><li>...
Cargo Cult Perl use CGI;
Comment Code Smells <ul><li>Non-O-O: </li></ul><ul><ul><li>Wannabee Objects and Data Clumps* </li></ul></ul><ul><ul><li>Cu...
Line Editing <ul><li>Reduce bloat </li></ul><ul><ul><li>Mothball code that coverage analysis indicates is not called </li>...
Consolidate Variables <ul><li>my (%hits_by_client, %hits_by_method, %hits_by_ext, </li></ul><ul><li>%hits_by_protocol, %hi...
Consolidate Variables <ul><li>my  %hits; </li></ul><ul><li>$hits{CLIENT}{$client}++; </li></ul><ul><li>$hits{METHOD}{$meth...
Consolidate Variables <ul><li>my ($client, $method, $extension, $protocol, $uri) = </li></ul><ul><li>($line =~ /^(S+) - .....
Consolidate Variables <ul><li>my @KEYS = qw(CLIENT METHOD EXTENSION PROTOCOL URI); </li></ul><ul><li>my %access; </li></ul...
Line Editing <ul><li>Remove rote stuff that the computer can figure out for you </li></ul><ul><ul><li>Example: </li></ul><...
Line Editing <ul><li>Try: </li></ul><ul><ul><li>my $sth = $dbh->prepare(&quot;INSERT INTO perf (s, s1a, s1b, x1, x3, y1, y...
Line Editing <ul><li>Still too much work, too error-prone. Try: </li></ul><ul><ul><li>$dbh->do( </li></ul></ul><ul><ul><li...
Line Editing <ul><li>This wheel has been invented several times, e.g.: </li></ul><ul><ul><li>use DBIx::Recordset; </li></u...
Line Editing <ul><li>Get rid of massive strings </li></ul><ul><li>Especially for HTML, use a templating system instead </l...
use strict <ul><li>Use it or get sand  kicked in your face </li></ul><ul><li>Eliminate all errors in order  to get the cod...
use warnings <ul><li>Use it or get sand kicked in your face </li></ul><ul><ul><li>Can use  -w  instead on older perls </li...
Commonly Neglected Modules <ul><li>Date:: *  - look for  unnecessary calls to  date  or  cal </li></ul><ul><li>DBI, DBD:: ...
Upcoming SlideShare
Loading in …5
×

Dealing with Legacy Perl Code - Peter Scott

14,193 views

Published on

Peter Scott, author of the O'Reilly School of Technology's Perl Programming Certificate series, talks about how to deal with "legacy" Perl code - written by someone else, or maybe even yourself when you were younger and less wise.

Published in: Technology
  • Login to see the comments

Dealing with Legacy Perl Code - Peter Scott

  1. 1. Maintaining Code While Staying Sane Peter Scott O’Reilly School of Technology February 2011
  2. 2. Dealing With Legacy Perl <ul><li>Legacy Perl can stink </li></ul><ul><li>Even when you wrote it </li></ul><ul><li>Or especially when you wrote it </li></ul><ul><li>But Why? </li></ul>
  3. 3. Why So Many Ugly Perl Programs? <ul><li>Unfortunately, some of those ways stink </li></ul><ul><li>Or, people use more than one way of doing the same thing in the same program </li></ul>
  4. 4. The “DWIM” Myth <ul><li>“ Perl programming doesn’t require the same discipline as other languages” </li></ul><ul><li>Indeed; it may require more </li></ul><ul><ul><li>So many WTDI </li></ul></ul><ul><li>Cure: Adopt best practices </li></ul>
  5. 5. The “Prototyping Only” Myth <ul><li>“ Perl is too slow and/or unpredictable to be used for serious work” </li></ul><ul><li>Too slow - sometimes, not always when you’d expect it </li></ul><ul><li>Unpredictable - only when programming without discipline or understanding </li></ul><ul><li>Cure: Learn algorithms, profiling, benchmarking </li></ul>
  6. 6. The “$@%*!” Myth <ul><li>“ Perl is a write-only language” </li></ul><ul><li>Another product of insufficient discipline </li></ul><ul><li>The dark side of TMTOWTDI </li></ul><ul><li>Cure: Adopt best practices, eschew obfuscation </li></ul>
  7. 7. Find the Author(s)! <ul><li>Are they a better programmer than you or worse? </li></ul><ul><li>Especially, better or worse at Perl? </li></ul><ul><li>This helps you evaluate code you don’t understand </li></ul><ul><ul><li>If you find code you don’t understand, it may be wrong </li></ul></ul><ul><ul><li>Or it may be right, and over your head </li></ul></ul><ul><li>What was their background? </li></ul><ul><ul><li>A Shell programmer uses different idioms from a C++ programmer </li></ul></ul>
  8. 8. What Are You Dealing With? <ul><li>What was the code optimized for? </li></ul><ul><ul><li>Maintainability </li></ul></ul><ul><ul><li>Performance </li></ul></ul><ul><ul><li>Brevity </li></ul></ul><ul><ul><li>Job security </li></ul></ul><ul><ul><li>Something else? </li></ul></ul>
  9. 9. Maintainability <ul><li># Print words with an even number of letters, AND even </li></ul><ul><li># number of each vowel, AND even position in the input </li></ul><ul><li># (input is a dictionary that has one word per line) </li></ul><ul><li>OUTER: while (<>) </li></ul><ul><li>{ </li></ul><ul><li>next if $. % 2; </li></ul><ul><li>chomp; </li></ul><ul><li>next if length() % 2; </li></ul><ul><li>for my $vowel (qw/a e i o u y/) </li></ul><ul><li>{ </li></ul><ul><li>my @vowels = /$vowel/g; </li></ul><ul><li>next OUTER if @vowels %2; </li></ul><ul><li>} </li></ul><ul><li>print &quot;$_n&quot;; </li></ul><ul><li>} </li></ul>
  10. 10. Performance <ul><li>while (<>) </li></ul><ul><li>{ </li></ul><ul><li>next if ($. | length() - 1)) % 2; </li></ul><ul><li>next if tr/e// % 2; </li></ul><ul><li>next if tr/a// % 2; </li></ul><ul><li>next if tr/i// % 2; </li></ul><ul><li>next if tr/o// % 2; </li></ul><ul><li>next if tr/u// % 2; </li></ul><ul><li>next if tr/y// % 2; </li></ul><ul><li>print; </li></ul><ul><li>} </li></ul>
  11. 11. Brevity <ul><li>#!/usr/bin/perl -ln </li></ul><ul><li>($x=aeiouy)=~s#.#y/$&//|#g;eval(&quot;$x$.|y///c&quot;)%2&&next;print </li></ul>
  12. 12. Job Security <ul><li>@i = map { chop; $x++ %2 ? $_ : () } <>; </li></ul><ul><li>while ($i = shift @i) </li></ul><ul><li>{ </li></ul><ul><li>ord(pack &quot;w/a*&quot;, $i) & 1 and next; </li></ul><ul><li>$_ = &quot;$in&quot;; </li></ul><ul><li>$i =~ s/$_(.*)$_/$1/ for qw/a e i o u y/; </li></ul><ul><li>print unless $i =~ /[aeiouy]/; </li></ul><ul><li>} </li></ul>
  13. 13. Testing <ul><li>You can’t test too early </li></ul><ul><ul><li>Or too much </li></ul></ul><ul><ul><li>Use Test::More </li></ul></ul><ul><li>Also useful: </li></ul><ul><ul><li>Test::Exception </li></ul></ul><ul><ul><li>Test::Inline </li></ul></ul><ul><ul><li>Test::NoWarnings </li></ul></ul>
  14. 14. Tests are Real Programs, Too <ul><li>Don’t abandon good indentation, variable naming, design, etc just because they’re “tests” </li></ul><ul><ul><li>Follow good development practices </li></ul></ul><ul><ul><li>use strict and use warnings in them </li></ul></ul><ul><ul><li>Abstract common code to modules in t/ </li></ul></ul><ul><li>Keep tests small </li></ul><ul><li>They’ll grow anyway </li></ul><ul><li>Refactor as necessary </li></ul><ul><li>It’s fine for tests to prompt for passwords, etc </li></ul>
  15. 15. Testing Web Applications <ul><li>Good design would mean you wouldn't have to go through a web server, of course </li></ul><ul><li>Start out simple by using WWW::Mechanize </li></ul><ul><ul><li>Acts like a virtual browser </li></ul></ul><ul><ul><li>Easy to navigate and fill in forms </li></ul></ul>
  16. 16. Web Testing Example <ul><li>my $ua = WWW::Mechanize->new; </li></ul><ul><li>my $res = $ua->get(&quot;http://www.example.com/&quot;); </li></ul><ul><li>ok( $res->is_success, &quot;Got first page&quot;) </li></ul><ul><li>or die $res->message; </li></ul><ul><li>$ua->set_visible($username, $password); </li></ul><ul><li>ok( $ua->submit->is_success, &quot;Logged in&quot; ) </li></ul><ul><li>or die $ua->res->message; </li></ul>
  17. 17. Modern Web Testing <ul><li>Now you can use Test::WWW::Mechanize </li></ul><ul><ul><li>$mech->get_ok(...) </li></ul></ul><ul><ul><li>$mech->title_like(...) </li></ul></ul><ul><ul><li>$mech->content_contains(...) </li></ul></ul><ul><ul><li>$mech->follow_link_ok(...) </li></ul></ul><ul><ul><li>$mech->has_tag_like(...) </li></ul></ul><ul><ul><li>etc </li></ul></ul>
  18. 18. Layout <ul><li>Code should be pretty to look at </li></ul><ul><li>Add comments where you had to think a lot </li></ul><ul><li>What’s your role? </li></ul><ul><li>Don’t reformat if it doesn’t belong to you </li></ul><ul><li>Use perltidy to fix up even the worst layout </li></ul>
  19. 19. Before perltidy <ul><li>for my $word (keys %{$word{$len}}){ </li></ul><ul><li>chop(my $prefix = $word);if ($opt{g}){ </li></ul><ul><li>while( $prefix ){ </li></ul><ul><li>if(my $words=delete$chain{ $prefix} ){ $chain{$word} = [ @$words, $word ]; </li></ul><ul><li>$maxcount=max ($maxcount,@$words+1); last;} </li></ul><ul><li>chop $prefix; } </li></ul><ul><li>}else{ if (my $words = delete </li></ul><ul><li>$chain{$prefix}){$chain{$word} = [@$words, </li></ul><ul><li>$word]; $changed = 1;} }} </li></ul>
  20. 20. After perltidy <ul><li>for my $word (keys %{$word{$len}}) { </li></ul><ul><li>chop(my $prefix = $word); </li></ul><ul><li>if ($opt{g}) { </li></ul><ul><li>while ($prefix) { </li></ul><ul><li>if (my $words = delete $chain{$prefix}) { </li></ul><ul><li>$chain{$word} = [@$words, $word]; </li></ul><ul><li>$maxcount = max($maxcount, @$words + 1); </li></ul><ul><li>last; </li></ul><ul><li>} </li></ul><ul><li>chop $prefix; </li></ul><ul><li>} </li></ul><ul><li>} </li></ul><ul><li>else { </li></ul><ul><li>if (my $words = delete $chain{$prefix}) { </li></ul><ul><li>$chain{$word} = [@$words, $word]; </li></ul><ul><li>$changed = 1; </li></ul><ul><li>} </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
  21. 21. After perltidy <ul><li>for my $word (keys %{$word{$len}}) </li></ul><ul><li>{ </li></ul><ul><li>chop(my $prefix = $word); </li></ul><ul><li>if ($opt{g}) </li></ul><ul><li>{ </li></ul><ul><li>while ($prefix) </li></ul><ul><li>{ </li></ul><ul><li>if (my $words = delete $chain{$prefix}) </li></ul><ul><li>{ </li></ul><ul><li>$chain{$word} = [@$words, $word]; </li></ul><ul><li>$maxcount = max($maxcount, @$words + 1); </li></ul><ul><li>last; </li></ul><ul><li>} </li></ul><ul><li>chop $prefix; </li></ul><ul><li>} </li></ul><ul><li>} </li></ul><ul><li>else </li></ul><ul><li>{ </li></ul><ul><li>if (my $words = delete $chain{$prefix}) </li></ul><ul><li>{ </li></ul><ul><li>$chain{$word} = [@$words, $word]; </li></ul><ul><li>$changed = 1; </li></ul><ul><li>} </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
  22. 22. Analysis <ul><li>Eliminate superfluous code through coverage analysis: </li></ul><ul><ul><li>Devel::Coverage </li></ul></ul><ul><ul><li>Devel::Cover </li></ul></ul><ul><li>Improve speed through profiling: </li></ul><ul><ul><li>Devel::Dprof </li></ul></ul><ul><ul><li>Devel::NYTProf </li></ul></ul>
  23. 23. Devel::NYTProf <ul><li>Very new </li></ul><ul><li>Incredibly flexible and accurate </li></ul><ul><li>Terrific reporting </li></ul>
  24. 24. Devel::NYTProf
  25. 25. What to Look Out For in Inherited Code <ul><li>Apparent level of Perl expertise </li></ul><ul><ul><li>Uses hashes? Regexes? </li></ul></ul><ul><ul><li>Uses parallel arrays/hashes instead of LoLs? </li></ul></ul><ul><ul><li>Calls unnecessary external programs? </li></ul></ul><ul><li>What version of Perl was it apparently developed for? </li></ul><ul><ul><li>Uses my ? Or local ? Uses use ? </li></ul></ul><ul><li>Cargo Cult Perl </li></ul>
  26. 26. The Documentation Hound <ul><li>########################################## </li></ul><ul><li># Function name: increment_number </li></ul><ul><li># Author: John Q. Lifer </li></ul><ul><li># Date Created: 1996-07-14 13:45:22 PDT </li></ul><ul><li># Last modified: 2005-03-21 11:09:32 PST </li></ul><ul><li># Inputs: Number </li></ul><ul><li># Outputs: None </li></ul><ul><li># Returns: Input number plus one </li></ul><ul><li># Exceptions: none </li></ul><ul><li># Change history: </li></ul><ul><li># 1996-07-21: Fixed off by one bug - jql </li></ul><ul><li># 2002-10-23: Changed obfuscatory ++ operator - jql </li></ul><ul><li>########################################## </li></ul><ul><li>sub increment_number { </li></ul><ul><li>### Formal parameter list </li></ul><ul><li>my ($num) = @_; </li></ul><ul><li>### Function body </li></ul><ul><li># TODO: Throw exception on missing input, NaN, etc... - jql </li></ul><ul><li>$num = $num + 1; # Add one to $num </li></ul><ul><li>return $num; </li></ul><ul><li>} </li></ul>
  27. 27. The Documentation Hound <ul><li>########################################## </li></ul><ul><li># Function name: increment_number </li></ul><ul><li># Author: John Q. Lifer </li></ul><ul><li># Date Created: 1996-07-14 13:45:22 PDT </li></ul><ul><li># Last modified: 2005-03-21 11:09:32 PST </li></ul><ul><li># Inputs: Number </li></ul><ul><li># Outputs: None </li></ul><ul><li># Returns: Input number plus one </li></ul><ul><li># Exceptions: none </li></ul><ul><li># Change history: </li></ul><ul><li># 1996-07-21: Fixed off by one bug - jql </li></ul><ul><li># 2002-10-23: Changed obfuscatory ++ operator - jql </li></ul><ul><li>########################################## </li></ul><ul><li>sub increment_number { </li></ul><ul><li>### Formal parameter list </li></ul><ul><li>my ($num) = @_; </li></ul><ul><li>### Function body </li></ul><ul><li># TODO: Throw exception on missing input, NaN, etc... - jql </li></ul><ul><li>$num = $num + 1; # Add one to $num </li></ul><ul><li>return $num; </li></ul><ul><li>} </li></ul>
  28. 28. The Documentation Hound Cure <ul><li>s/^#+n//mg </li></ul><ul><li>Move to POD later in this file </li></ul><ul><ul><li>Or maybe another file </li></ul></ul><ul><ul><ul><li>Such as /dev/null </li></ul></ul></ul><ul><ul><li>But keep function/method signatures and descriptions </li></ul></ul><ul><li>Make the code tell the story </li></ul><ul><li>Preserve comments that answer 'Why?' </li></ul>
  29. 29. The Documentation Hound Cure <ul><li>For local programs, author and history information can be taken care of by a source code control system </li></ul><ul><li>For distributions: </li></ul><ul><ul><li>Move author information to a README or POD AUTHOR section </li></ul></ul><ul><ul><li>Move history information to change log </li></ul></ul>
  30. 30. String Manipulation, BASIC-Style <ul><li>$repl = ' '; </li></ul><ul><li>for ($off = 0; $off < length($str); $off++) { </li></ul><ul><li>$c = substr($str, $off, 1); </li></ul><ul><li>if (index(&quot;012345789&quot;, $c) < 0) { </li></ul><ul><li>substr($str, $off, 1, $repl); </li></ul><ul><li>$off-- unless $repl; </li></ul><ul><li>$repl = ''; </li></ul><ul><li>} </li></ul><ul><li>else { </li></ul><ul><li>$repl = ' '; </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
  31. 31. … i.e., Without Regexes <ul><li>$str =~ s/D+/ /g; </li></ul>
  32. 32. Nice Formatting, But… <ul><li>if ($month == &quot;1&quot;) { $month = &quot;0&quot; . $month; } </li></ul><ul><li>if ($month == &quot;2&quot;) { $month = &quot;0&quot; . $month; } </li></ul><ul><li>if ($month == &quot;3&quot;) { $month = &quot;0&quot; . $month; } </li></ul><ul><li>if ($month == &quot;4&quot;) { $month = &quot;0&quot; . $month; } </li></ul><ul><li>if ($month == &quot;5&quot;) { $month = &quot;0&quot; . $month; } </li></ul><ul><li>if ($month == &quot;6&quot;) { $month = &quot;0&quot; . $month; } </li></ul><ul><li>if ($month == &quot;7&quot;) { $month = &quot;0&quot; . $month; } </li></ul><ul><li>if ($month == &quot;8&quot;) { $month = &quot;0&quot; . $month; } </li></ul><ul><li>if ($month == &quot;9&quot;) { $month = &quot;0&quot; . $month; } </li></ul>
  33. 33. Nice Formatting, But… <ul><li>$month = sprintf &quot;%02d&quot;, $month; </li></ul>
  34. 34. Too Much Time on Their Hands <ul><li>$smtp->datasend(&quot;To: santa.claus@north.polen&quot;); </li></ul><ul><li>$smtp->datasend(&quot;From: johnny@homen&quot;); </li></ul><ul><li>$smtp->datasend(&quot;Subject: I've Been Goodn&quot;); </li></ul><ul><li>$smtp->datasend(&quot;n&quot;); </li></ul><ul><li>$smtp->datasend(&quot;Dear Santan&quot;); </li></ul><ul><li>$smtp->datasend(&quot;For Christmas I would like:n&quot;); </li></ul><ul><li>$smtp->datasend(&quot; Perl 6n&quot;); </li></ul><ul><li>$smtp->datasend(&quot;Thank youn&quot;); </li></ul>
  35. 35. Too Much Time on Their Hands <ul><li>$smtp->datasend(<<'EOTEXT'); </li></ul><ul><li>To: santa.claus@north.pole </li></ul><ul><li>From: johnny@home </li></ul><ul><li>Subject: I've Been Good </li></ul><ul><li>Dear Santa </li></ul><ul><li>For Christmas I would like: </li></ul><ul><li>Perl 6 </li></ul><ul><li>Thank you </li></ul><ul><li>EOTEXT </li></ul>
  36. 36. Too Much Time on Their Hands <ul><li>$smtp->datasend(<<'EOTEXT' =~ /[^Sn]*(.*?n)/g); </li></ul><ul><li>To: santa.claus@north.pole </li></ul><ul><li>From: johnny@home </li></ul><ul><li>Subject: I've Been Good </li></ul><ul><li>Dear Santa </li></ul><ul><li>For Christmas I would like: </li></ul><ul><li>Perl 6 </li></ul><ul><li>Thank you </li></ul><ul><li>EOTEXT </li></ul>
  37. 37. <ul><li>$smtp->datasend(<<'EOTEXT' =~ /[^Sn]*(.*?n)/g); </li></ul><ul><li>EOTEXT </li></ul>Too Much Time on Their Hands To: santa.claus@north.pole From: johnny@home Subject: I've Been Good Dear Santa For Christmas I would like: Perl 6 Thank you <ul><li>Or use, say, Text::Outdent or similar </li></ul>
  38. 38. Way Too Much Time On Their hands <ul><li>print &quot;<HTML><HEAD>n&quot;; </li></ul><ul><li>print &quot;<TITLE>My Home Page</TITLE>n&quot;; </li></ul><ul><li>print &quot;</HEAD><BODY>n&quot;; </li></ul><ul><li>print &quot;<H1>My Home Page</H1>n&quot;; </li></ul><ul><li>print &quot;<H2>What I Did Last Summer</H2>n&quot;; </li></ul><ul><li>print &quot;<H3>by Cuthbert J. Bigglesworth</H3>n&quot;; </li></ul><ul><li>print &quot;<P>Me and my dog <I>Fang</I> went down &quot;; </li></ul><ul><li>print &quot;to the river and caught toads.</P>n&quot;; </li></ul><ul><li>print &quot;<P>P.S. I also learned Perl.</P>n&quot;; </li></ul><ul><li>print &quot;<P>Here is a scalar: <KBD>$x</KBD>.</P>n&quot;; </li></ul><ul><li>print &quot;</BODY></HTML>n&quot;; </li></ul><ul><li>Use HTML::Template, Text::Template, the Template Toolkit, or Inline::Files </li></ul>
  39. 39. <ul><li>my ($sec, $min, $hour, </li></ul><ul><li>$mday, $mon, $year, </li></ul><ul><li>$wday, $yday, $isdst) </li></ul><ul><li>= localtime(time); </li></ul>The Perils of Cut and Paste # But now use only $mon and $mday...
  40. 40. <ul><li>my ($mday, $mon) </li></ul><ul><li>=(localtime)[4,3]; </li></ul>The Perils of Cut and Paste
  41. 41. <ul><li>my ($mday, $mon) </li></ul><ul><li>= (localtime)[4,3]; </li></ul>The Perils of Cut and Paste
  42. 42. <ul><li>use Time::localtime; </li></ul><ul><li>my ($mday, $mon) </li></ul><ul><li>= (localtime->mday, localtime->mon); </li></ul>The Perils of Cut and Paste
  43. 43. Scope? What is This Thing You Call Scope? <ul><li>my ($count, $ncount, @recs, @nrecs, %ccname, %ccphone, %ccaddr, %cccity) </li></ul><ul><li>my ($count2, $temp, @vbinfo, @pscan, $is_true); </li></ul><ul><li>my $temp2; </li></ul><ul><li>my $tempcount; </li></ul><ul><li>my $fudgeFactor; </li></ul><ul><li>my ($fname, $lname, $mi, $address1, $address2, $city, $state, $country, $c_code, $phone, $email, $email_valid); </li></ul><ul><li>my ($form1, $form2, $form3, $form4, $form4a, $form4b, $form4b_valid, @subtotals, $preTaxTotal, $postTaxTotal, $shipping, $TotalTotal); </li></ul><ul><li>my ($is_valid, $discount, $mealpref, $likes_pie, $whatisthisfor); </li></ul><ul><li>my ($PI, $PIE) = (3.14159265358979, &quot;cherry&quot;); </li></ul><ul><li>$count2 = 3; </li></ul><ul><li>[...] </li></ul>
  44. 44. Scope Ignorance Cure <ul><li>Move variable declaration to latest possible point </li></ul><ul><ul><li>Exception: configuration settings </li></ul></ul><ul><ul><ul><li>Put those in a separate file if appropriate </li></ul></ul></ul><ul><li>Use in-line declarations for loop variables: </li></ul><ul><ul><li>foreach my $dog (@schnauzers) </li></ul></ul><ul><ul><li>while (my $imp = shift @demons) </li></ul></ul><ul><li>You can carry this even further: </li></ul><ul><ul><li>getopts('dq:v', my %Opt); </li></ul></ul><ul><li>Scope Ignorance is frequently combined with Monolithic Madness </li></ul>
  45. 45. Monolithic Madness <ul><li>(Visualize 2500 lines of code without the word 'sub') </li></ul><ul><li>Sufferers’ favorite language: JCL </li></ul><ul><li>“ It started out at 30 lines… it just grew” </li></ul><ul><li>“ I know where everything is” </li></ul><ul><ul><li>Of course, no one else does </li></ul></ul>
  46. 46. Monolithic Madness Cure <ul><li>Look for variables with short scopes and evaluate the area for subroutine-ness </li></ul><ul><li>If you like Eclipse, try Devel::Refactor and the extract_subroutine method for the EPIC plug-in </li></ul><ul><li>use strict and turn it off over an ever-narrowing scope: </li></ul><ul><ul><li>use strict; </li></ul></ul><ul><ul><li>[...] </li></ul></ul><ul><ul><li>{ </li></ul></ul><ul><ul><li>no strict; </li></ul></ul><ul><ul><li>[...] </li></ul></ul><ul><ul><li>} </li></ul></ul><ul><ul><li>[...] </li></ul></ul>
  47. 47. Perl from ??? <ul><li>$#abspaths = $num; </li></ul><ul><li>for ($i=0; $i<$num; $i++) { </li></ul><ul><li>my $newlen = </li></ul><ul><li>$ROOTLEN+1+length($paths[$i]); </li></ul><ul><li>$abspaths[$i] = ' ' x $newlen; </li></ul><ul><li>$abspaths[$i] = sprintf(&quot;%s/%s&quot;, $ROOT, </li></ul><ul><li>$dirs[$i]); </li></ul><ul><li>} </li></ul>
  48. 48. Perl from C <ul><li>abspaths = realloc(abspaths, </li></ul><ul><li>num * sizeof(char*)); </li></ul><ul><li>for ( i=0; i< num; i++) { </li></ul><ul><li>int newlen = </li></ul><ul><li>ROOTLEN+1+ strlen(paths[ i]); </li></ul><ul><li>char temp[newlen]; </li></ul><ul><li>abspaths[ i] = malloc(newlen); </li></ul><ul><li>sprintf(abspaths[i], &quot;%s/%s&quot;, ROOT, </li></ul><ul><li>dirs[ i]); </li></ul><ul><li>} </li></ul>
  49. 49. Perl from C <ul><li>@abspaths = map { &quot;$ROOT/$_&quot; } @paths; </li></ul>
  50. 50. Perl from ??? <ul><li>$file = &quot;matrix.dat&quot;; </li></ul><ul><li>open (FH, &quot;>$file&quot;); </li></ul><ul><li>for ($I = 1, $I <= 4; $I++) { </li></ul><ul><li>$value = $X[$I][$_], </li></ul><ul><li>write (FH) for 1..10; </li></ul><ul><li>} </li></ul><ul><li>format FH = </li></ul><ul><li><<<<<<<<<<<<< </li></ul><ul><li>$value </li></ul><ul><li>. </li></ul><ul><li>close (FH); </li></ul><ul><li>exit &quot;Done&quot;; </li></ul>
  51. 51. Perl from FORTRAN <ul><li>ofile = &quot;matrix.dat&quot; </li></ul><ul><li>OPEN (42, FILE=ofile) </li></ul><ul><li>DO 10 I = 1, 4 </li></ul><ul><li>10 WRITE (42,100) (X(I,J),J=1,10) </li></ul><ul><li>100 FORMAT &quot;(10F10.3)&quot; </li></ul><ul><li>CLOSE (42) </li></ul><ul><li>STOP &quot;Done&quot; </li></ul>
  52. 52. Perl from FORTRAN <ul><li>open my $fh, '>', $file or die $!; </li></ul><ul><li>for my $i (1 .. 4) { </li></ul><ul><li>printf {$fh} &quot;%10.3f&quot; $X[$i][$_] </li></ul><ul><li>for 1..10; </li></ul><ul><li>print {$fh} &quot;n&quot;; </li></ul><ul><li>} </li></ul>
  53. 53. Perl from ??? <ul><li>###################### </li></ul><ul><li>#Program name: Report. </li></ul><ul><li>###################### </li></ul><ul><li>sub decipher { </li></ul><ul><li>unpack $fmt, shift; </li></ul><ul><li>} </li></ul><ul><li>$fmt = &quot;A7&quot; . # base </li></ul><ul><li>&quot;x4&quot; . # filler </li></ul><ul><li>&quot;A7&quot;; # bonus </li></ul><ul><li>$rec = <>; </li></ul><ul><li>($base, $bonus) = decipher($rec); </li></ul><ul><li>$salary = $base + $bonus; </li></ul><ul><li>printf &quot;%7.2fn&quot;, $salary; </li></ul><ul><li>exit; </li></ul>
  54. 54. Perl from COBOL <ul><li>IDENTIFICATION DIVISION. </li></ul><ul><li>PROGRAM-ID. Report. </li></ul><ul><li>DATA DIVISION. </li></ul><ul><li>WORKING-STORAGE SECTION. </li></ul><ul><li>01 salary PICTURE 99999V99 </li></ul><ul><li>01 rec </li></ul><ul><li>02 base PICTURE 99999V99 </li></ul><ul><li>02 FILLER PICTURE X(4) </li></ul><ul><li>02 bonus PICTURE 99999V99 </li></ul><ul><li>PROCEDURE DIVISION. </li></ul><ul><li>READ rec </li></ul><ul><li>ADD bonus TO base GIVING salary </li></ul><ul><li>DISPLAY salary </li></ul><ul><li>STOP RUN. </li></ul>
  55. 55. Perl from COBOL <ul><li>my $rec = <>; </li></ul><ul><li>my ($bonus, $base) = unpack &quot;A7x4A7&quot;, $rec; </li></ul><ul><li>my $salary = $bonus + $base; </li></ul><ul><li>printf &quot;%7.2fn&quot;, $salary; </li></ul>
  56. 56. Perl from ??? <ul><li>#!/usr/bin/perl -l </li></ul><ul><li>print &quot;Think of a number: &quot;; $dummy = <>; </li></ul><ul><li>$I = 1; </li></ul><ul><li>AGAIN: print &quot;Is it &quot;,$I, &quot;?&quot;; </li></ul><ul><li>$A = <>; </li></ul><ul><li>$X = substr($A,0,1); </li></ul><ul><li>if($X eq&quot;Y&quot; or $X eq&quot;y&quot;) { goto DONE } </li></ul><ul><li>$I =$I + 1 </li></ul><ul><li>goto AGAIN; </li></ul><ul><li>DONE: exit; </li></ul><ul><li># END </li></ul>
  57. 57. Perl from BASIC <ul><li>100 INPUT &quot;Think of a number: &quot;;D$ </li></ul><ul><li>110 LET I = 1 </li></ul><ul><li>120 PRINT &quot;Is it &quot;, I; </li></ul><ul><li>130 INPUT &quot;?&quot;; A$ </li></ul><ul><li>140 LET X$ = LEFT$(A$,1) </li></ul><ul><li>145 IF X$ = &quot;Y&quot; OR X$ = &quot;y&quot; THEN GOTO 149 </li></ul><ul><li>147 LET I = I + 1 </li></ul><ul><li>148 GOTO 120 </li></ul><ul><li>149 STOP </li></ul><ul><li>150 END </li></ul>
  58. 58. Perl from BASIC <ul><li>print &quot;Think of a numbern&quot;; </li></ul><ul><li>my $ans = ''; </li></ul><ul><li>for (my $guess = 1; $ans !~ /^y/i; $guess++) { </li></ul><ul><li>print &quot;Is it $guess? &quot;; </li></ul><ul><li>chomp($ans = <>); </li></ul><ul><li>} </li></ul>
  59. 59. <ul><li>read(STDIN, $buffer, $ENV{CONTENT_LENGTH}); </li></ul><ul><li>my @pairs = split(/&/, $buffer); </li></ul><ul><li>push(@pairs, map { split(/&/, $_) } $ENV{QUERY_STRING}); </li></ul><ul><li>push(@pairs, map { split(/&/, $_) } @ARGV); </li></ul><ul><li>foreach my $pair (@pairs) { </li></ul><ul><li>my ($name, $value) = split(/=/, $pair); </li></ul><ul><li>$name =~ tr/+/ /; </li></ul><ul><li>$name =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack(&quot;C&quot;, hex($1))/eg; </li></ul><ul><li>$value =~ tr/+/ /; </li></ul><ul><li>$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack(&quot;C&quot;, hex($1))/eg; </li></ul><ul><li>[... You know the rest... ] </li></ul>Cargo Cult Perl <ul><li>Stop the insanity! </li></ul>
  60. 60. Cargo Cult Perl use CGI;
  61. 61. Comment Code Smells <ul><li>Non-O-O: </li></ul><ul><ul><li>Wannabee Objects and Data Clumps* </li></ul></ul><ul><ul><li>Cut And Paste </li></ul></ul><ul><li>O-O: </li></ul><ul><ul><li>Mixed Abstraction Levels </li></ul></ul><ul><ul><li>Time Dependencies* </li></ul></ul><ul><li>* Courtesy of “The Art of Agile Development”, Shore & Warden </li></ul>
  62. 62. Line Editing <ul><li>Reduce bloat </li></ul><ul><ul><li>Mothball code that coverage analysis indicates is not called </li></ul></ul><ul><ul><li>Shorten subroutines and main program to one screen’s length at most </li></ul></ul><ul><ul><li>Don’t exceed screen width </li></ul></ul>
  63. 63. Consolidate Variables <ul><li>my (%hits_by_client, %hits_by_method, %hits_by_ext, </li></ul><ul><li>%hits_by_protocol, %hits_by_uri); </li></ul><ul><li>$hits_by_client{$client}++; </li></ul><ul><li>$hits_by_method{$method}++; </li></ul><ul><li>$hits_by_ext{$extension}++; </li></ul><ul><li>$hits_by_protocol{$protocol}++; </li></ul><ul><li>$hits_by_uri{$uri}++; </li></ul>
  64. 64. Consolidate Variables <ul><li>my %hits; </li></ul><ul><li>$hits{CLIENT}{$client}++; </li></ul><ul><li>$hits{METHOD}{$method}++; </li></ul><ul><li>$hits{EXTENSION}{$extension}++; </li></ul><ul><li>$hits{PROTOCOL}{$protocol}++; </li></ul><ul><li>$hits{URI}{$uri}++; </li></ul>
  65. 65. Consolidate Variables <ul><li>my ($client, $method, $extension, $protocol, $uri) = </li></ul><ul><li>($line =~ /^(S+) - .../); </li></ul><ul><li>my %hits; </li></ul><ul><li>$hits{CLIENT}{$client}++; </li></ul><ul><li>$hits{METHOD}{$method}++; </li></ul><ul><li>$hits{EXTENSION}{$extension}++; </li></ul><ul><li>$hits{PROTOCOL}{$protocol}++; </li></ul><ul><li>$hits{URI}{$uri}++; </li></ul>
  66. 66. Consolidate Variables <ul><li>my @KEYS = qw(CLIENT METHOD EXTENSION PROTOCOL URI); </li></ul><ul><li>my %access; </li></ul><ul><li>@access{@KEYS} = ($line =~ /^(S+) - .../); </li></ul><ul><li>my %hits; </li></ul><ul><li>for my $key (@KEYS) { </li></ul><ul><li>$hits{$key}{ $access{$key} }++; </li></ul><ul><li>} </li></ul>
  67. 67. Line Editing <ul><li>Remove rote stuff that the computer can figure out for you </li></ul><ul><ul><li>Example: </li></ul></ul><ul><ul><li>$dbh->do(&quot;INSERT INTO perf (s, s1a, s1b, x1, x3, y1, y2, y3, zx, zx4, zx4a) VALUES ($s, $s1a, '$s1b', '$x1', $x2, $y1, $y2, $y3, $zx, '$zx4, '$zx4a')&quot;); </li></ul></ul><ul><ul><li>Arrgh </li></ul></ul>
  68. 68. Line Editing <ul><li>Try: </li></ul><ul><ul><li>my $sth = $dbh->prepare(&quot;INSERT INTO perf (s, s1a, s1b, x1, x3, y1, y2, y3, zx, zx4, zx4a) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)&quot;); </li></ul></ul><ul><ul><li>$sth->execute($s, $s1a, $s1b, $x1, $x3, $y1, $y3, $y2, $zx, $zx4, $zx4a); </li></ul></ul>
  69. 69. Line Editing <ul><li>Still too much work, too error-prone. Try: </li></ul><ul><ul><li>$dbh->do( </li></ul></ul><ul><ul><li>make_insert(perf => keys %data), </li></ul></ul><ul><ul><li>undef, values %data); </li></ul></ul><ul><ul><li>sub make_insert </li></ul></ul><ul><ul><li>{ </li></ul></ul><ul><ul><li>my ($table, @cols) = @_; </li></ul></ul><ul><ul><li>&quot;INSERT INTO $table (&quot; </li></ul></ul><ul><ul><li>. join(',' => @cols) . &quot;) VALUES (&quot; </li></ul></ul><ul><ul><li>. join(',' => ('?') x @cols) . &quot;)&quot;; </li></ul></ul><ul><ul><li>} </li></ul></ul>
  70. 70. Line Editing <ul><li>This wheel has been invented several times, e.g.: </li></ul><ul><ul><li>use DBIx::Recordset; </li></ul></ul><ul><ul><li># ... </li></ul></ul><ul><ul><li>DBIx::Recordset->insert( { </li></ul></ul><ul><ul><li>'!DataSource' => $dbh, </li></ul></ul><ul><ul><li>'!Table' => 'perf', </li></ul></ul><ul><ul><li>%data } ); </li></ul></ul>
  71. 71. Line Editing <ul><li>Get rid of massive strings </li></ul><ul><li>Especially for HTML, use a templating system instead </li></ul><ul><ul><li>HTML::Template works great, even for non-HTML </li></ul></ul><ul><ul><li>So does Text::Template and the Template Toolkit </li></ul></ul>
  72. 72. use strict <ul><li>Use it or get sand kicked in your face </li></ul><ul><li>Eliminate all errors in order to get the code to run </li></ul><ul><li>Eliminate unnecessary package variables </li></ul><ul><li>Declare lexical variables explicitly </li></ul><ul><li>Eliminate symbolic references </li></ul><ul><ul><li>They’re hard to maintain anyway and just plain ugly </li></ul></ul><ul><li>Turn strictness off with no strict </li></ul><ul><ul><li>I’ve only ever needed no strict 'refs' </li></ul></ul>
  73. 73. use warnings <ul><li>Use it or get sand kicked in your face </li></ul><ul><ul><li>Can use -w instead on older perls </li></ul></ul><ul><ul><li>On newer perls, use -W in testing to force warnings on across all modules </li></ul></ul><ul><li>Leave warnings enabled in production </li></ul><ul><ul><li>But if users might see the warnings, have them sent to you instead </li></ul></ul><ul><ul><li>Trap via $SIG{__WARN__} handler </li></ul></ul>
  74. 74. Commonly Neglected Modules <ul><li>Date:: * - look for unnecessary calls to date or cal </li></ul><ul><li>DBI, DBD:: * - look for unnecessary calls to database programs </li></ul><ul><li>LWP:: * - look for unnecessary calls to lynx , wget or GET </li></ul>

×