I don't want to be forced into your damned warning policy

Alias on 2010-04-08T07:58:35

Update: I am informed that while I may imply a timeline which sees the pragma modules mentioned below taking action before Moose, time-wise Moose acted first and then the pragma modules came later

Last weekend in my first round comparison between Mojo and Dancer, I noted that neither project used strict or warnings.

At the time, I suspected this was done for clarity. After all, it can get annoying when use strict and use warnings to get in the way of having a nice a clean synopsis.

It was too my great surprise that I discovered both web frameworks had decided that use strict and use warnings were good enough for everybody and they would silently turn both of them on.

This makes them the third group of modules to decide how I should write my code.

First are your $Adjective::Perl style modules.

This I can live with and playing with pragmas seems quite reasonable, since it's a style pragma module itself. By saying "use perl5i" I'm explicitly buying into their view of what code should be written and formatted like.

Then Moose decided that they would turn on strict and warnings as well.

This makes me a bit uncomfortable, since I use Moose for it's object model. I don't really want it imposing it's views on how I should write the rest of my code.

I can hear you already saying "But wait! You can turn it off with no warnings, but you shouldn't do that because it's best practice (and Best Practice) to always have them both on, and anyway it's only enabled until you say "no Moose;"

Or is it? That alone is an interesting question.

Do Moose's views on strictness and warnings able to escape it's scope, and will be imposed on me even when I tell it to go away with no Moose;

Or if they do go away, does that mean I've accidentally been running a whole bunch of code without strict and warnings on by mistake?

But I digress, now where was I... oh right!

<rant>

I appreciate you are trying to be nice and save me two lines, but dammit I'm not paying you (metaphorically) for that, and now I have to THINK instead because the LACK of an option to your code can be meaningful. It's worse than meaningful whitespace, it's a meaningful unknown. And I can trivially automate the production of those "use strict;" or "use strict;\nuse warnings;" (as you prefer) lines in pretty much any hackable editor written in Perl. Automating the thinking you have to do when there ISN'T something in the code is much harder, or impossible.

This kind of thing with Exporter is one of the (four) provably impossible problems that prevent Perl being parsable. Gee thanks!

From a perception point of view it's the same kind of situation when a Media Company announces they are going to buy a Mining Company. Why? Because the Mining Company has a lot of cash but little revenue, and the Media Company has a lot of revenue but little cash, so they'd "Go well together".

Before I say any more, you should already be a bit suspicious. And it's probably no surprise when you find out that the part-owner boss of the Media Company is also a part-owner of the Mining Company.

But that kind of thing is an obvious form of Conflict Of Interest. Humans are almost universally tuned to spot that kind of thing and see it as a negative.

It's a much trickier situation when the conflict is between Doing Your Job and things like Trying To Be Nice, or things like Clearly You Probably Meant It, So I'll Just Silently Correct That For You. There's a variety of meme's in this situation, different mixed perceptions based on your own personal morality.

But that doesn't remove the technical issue that you've conflated two entirely different functions into one module.

So now with Moose if I don't want warnings, but I do want strict, I'm not sure if I need to do this...

use Moose;
no warnings;

...

no Moose;
or this...
use Moose;

# Lets say I'm nice and allow all my Moose definition code # to follow their warnings policy, because I like their rigourous approach.

no Moose; use strict; use warnings;


Neither of these things are particularly pretty, but I'm stuck with the situation because there's a conflict of interest. The Moose authors choose to impose their views in an area outside of their scope, because it's convenient for them and saves them a couple of lines, And Besides Everyone Should Do It That Way.

But as long as it's only pragma modules that change pragmas (plus Moose) it's just an idiosyncracy, one of those weird little things modules do sometimes.

Except now we have another problem, because now It's A Trend. Everyone famous is doing it. Clearly it's The Right Thing.

So now we see web frameworks doing it. Mojo does it. Dancer does it.

Clearly it's the right thing to do, because chromatic and Schwern and Damian and the Moose cabal are doing it so it must be awesome.

At this point, you're probably preparing a snarky comment about how I'm just curmudgeonly and nit-picking. How I should only turn off warnings when I know a warnings is going to happen inside a SCOPE: { no warnings; ... } and how we need to set a good example for the less experienced people, and how YOU always want to see the warnings in production, and how warnings create bug reports so you fix more bugs, and so on.

But what about practical issues? What about situations where you need to do big things, complicated things, large scale things in one or many of the dimensions of width, or throughput, or reliability or complexity or code size.

Over the last 10 years, in which 90% of my paid work has been on websites, I can recall three situations in which I found truly important warnings on a production web server that I didn't find in any testing, and that would have led me to a fix I otherwise would have overlooked.

I've found tons of exceptions on production sure, but not that many warnings that mattered.

However, in that same 10 years, I've seen the opposite situation 6-8 times.

I've seen sysadmins blank out a config variable the wrong way, resulting in an undef where there should have been a value, which is checked in an "eq" comparison 20 times per page, each of which produced 2-3k of log file.

Or worse, I've seen this in a foreach ( ... ) { if ( undef eq 'string' ) { ... } } which is operating on several hundred or thousand entries.

Half the time, this happened because someone in the same office at the same time you are at work touched something they should have and uncovered it.

And when you see the load graph on the box spike the next day, you investigate and find it compressing 20gig of log files, all of which contain the same identical warning printed 40 or 50 million times.

But if you aren't so lucky, it happens on a weekend, or at night, or you haven't set something up right in Nagios, and now you've filled your machine's entire /var partition over the weekend, which prevented 2 or 3 other services that need /var too from working, which brought down the service, or the server.

I've seen 10 machine clusters running at high volume overflow every log partition in the cluster at a rate of a gig per host per minute because a telco outage at night caused a minor backend service to fail, which returned a single status string that wasn't checked for defined'ness to go undef and that status hash value was checked in the hot loop.

I've seen horrible UDP network syslog storms, and boxes dead so fast the Nagios Poll -> Human Alert -> Getting Online lag of 15 minutes wasn't enough to catch it and prevent it.

All of it because in a codebase of 50,000 or 100,000 lines, you only have to miss ONE thing in the wrong place to produce a warning. And nobody's perfect.

Now, by all means I encourage development with warnings on. And I absolutely think warnings should always be on in your test suite, with Test::NoWarnings enabled wherever possible for good measure, and a BEGIN { $^X = 1 } in the test header to really make sure warnings are aggressive.

If there's a configuration option for it, I'll even leave them on through User Acceptance Testing and Fuzz Testing and Penetration Testing and Load Testing and anything else that isn't Production.

In production I don't want to know about mistakes.

Well, that's not entirely true...

I want to know about mistakes in production, but what I want more is that production absolutely positively NEVER goes down. There's no debugging convenience in the world that should result in even the slight risk of turning into a Denial of Service.

The Spice MUST Flow.

If I go down in production, I want it to be for a reason that has never happened before. Ideally involve a three or four factor failure.

If a plane crashes into a telco NOC, triggering a complete network outage on my side of the city, and we switch to the disaster recovery site but the power ripples from the plane crash caused a transformer to blow, and the generator fails after 20 minutes because of a critical heat event due to a bird nest in the radiator catching on fire, because the maintenance man was on paternity 2 month paternity leave and the stand-in techie doing his job wasn't legally qualified to be on the roof with the electrical gear, THAT I can live with.

If an East European mafia takes a shining to me as a blackmail target, and initiates a 50gig/sec botnet distribution denial of service attack and we haven't set up the DDOS-protection contract because the financial crisis caused our budget to be cut this year, well that I can live with too.

If I can't afford any of that fancy stuff, but the Facebook utility my shoe-string budget startup created turns out to be too popular and despite my best efforts to keep it blazingly fast to prevent this kind of thing the success overloads my server someone just dropped the default Ubuntu on because we needed it up quickly, hey I'm happy to have that kind of problem.

Compared to these kinds of reasons for going down, having a volunteer website administrator who lives in Europe fiddle a setting they shouldn't have while I was asleep, and having the 500gig hard disk overflow with the same identical message repeated over and over 5 billion times really doesn't cut it.

And this same kind of thing seems to happen over and over again about once every 14 months, and only half the time am I lucky enough to do something about it in time.

When my object model is forcing warnings on me, and my web framework is forcing warnings on me, what am I supposed to do?
package Foo;

use MyWebFramework; no warnings;

use Moose; no warnings;

use Some::Random::Module; no warnings; # Can I really be sure they don't enable warnings? ...


Am I supposed to repeat this in every single class?

What about when I want warnings ON, now what?

Unlike exceptions, it's way way harder to catch and manage warnings, to force them always on and force them always off when you are in different environments.

The only way I know of to reliable distinguish between maximum noise and diagnostics and explosions in dev/test/uat, and no noise at all in production (except properly managed exceptions) is to have the code NOT use warnings, and then force it on from the top down in the right environments.

We've seen similar things before, stuff that starts out simple and obvious but just causes pain.

The @EXPORT array was, I'm sure, just a fine idea when it was added. It lets you import whole swathes of functions into your program without that annoying typing.

Of course, since now ANYBODY can fuck with it, if you are trying to write robust code you need to do stupid annoying things like this to avoid accidentally polluting your code.
use Carp          ();
use Cwd           ();
use File::Spec    ();
use File::HomeDir ();
use List::Util    ();
use Scalar::Util  ();
use Getopt::Long  ();
use YAML::Tiny    ();
use DBI           ();
use DBD::SQLite   ();
Why do I need to do that stupid braces shit? Because the alternative is I have to audit every single dependency to make sure it doesn't export by default, and THEN I have to also trust/hope they don't start exporting by default in the future.

Loading modules the safe and scalable way means doing MORE work than the unsafe and unscalable way.

DBI gets it right. The default way of using DBI that is documented everywhere is superficially more verbose and anal retentive than I need for simple things.

But as the code gets bigger, the code keeps working just as well and just as safely. I would hypothesise that this diligence on the part of DBI and Tim Bunce has in a single stroke kept Perl web applications industry-wide almost entirely free of SQL injection attacks.

The savings in terms of just the admin workload and security spending and security-forced upgrades done on overtime on the first Tuesday of every month have probably justified Tim's entire career.

Has default-import really given us such a large benefit that it overcomes all the times people have to type () and resolved clashing imports of corrupted OO APIs? Is the time saved not having to type ':ALL' really worth all that?

I say no.

And I say that this growing nascent fad to screw around with my pragmas when your module isn't actually a pragma itself needs to be nipped in the bud before it gets worse.

</rant>

While this is perhaps a controversial position (and so it won't be factored into the scoring as part of the competition) I have to say I was greatly impressed that the Dancer guys have offered to implement some kind of configuration option so I can explicitly turn disable their Dancer-imposed warnings in production (which at least mitigates the worst Real World problem, while retaining the magic pragma behaviour).


Nothing controversial that I see

Illusori on 2010-04-08T08:57:11

I don't think you've taken a controversial position at all there, you've hit the nail on the head.

Modules should stay the hell out of your namespace and application except to do the things you've actually asked them to do.

Warnings and logfiles: abso-bloody-lutely.

If you break something, you need to be told about it once, not 5 million times.

If someone else breaks something and doesn't pay attention that they're being told 5 million times, you don't want to find the 5 billion other messages first thing monday morning.

It's a horrible horrible mess, and anything that makes it worse needs stomping on fast.

what about catching the warnings?

gabor on 2010-04-08T09:12:12

Something like this:

my %warnings; $SIG{WARN} = sub { print STDERR $_[0] if not $warnings{$_[0]}; $warnings{$_[0]} = 1};

Gabor

Re:what about catching the warnings?

Illusori on 2010-04-08T12:32:32

Only works per-process.

Most webservers are multiple processes with a limited lifetime (they often get killed after a fixed number of requests).

Then there's the issue that most large sites have a server farm too...

Re:what about catching the warnings?

Tim Bunce on 2010-04-08T12:56:39

Yes, it only works per-process and per-server, but in my experience (with 80+ servers running many mod_perl processes) it is a very simple and effective way to eliminating the kinds of web-service log-storms you described. It changes an unmanageable flood into a very manageable stream. Highly recommended.

Re:what about catching the warnings?

Illusori on 2010-04-08T17:55:44

Sorry, didn't mean to imply that it doesn't mitigate the problem, it does but it can only mitigate it.

Whether that turns the situation into a manageable one or not would depend on circumstance. :)

Re:what about catching the warnings?

mgrimes on 2010-04-08T20:14:43

Does this work? I get an error:

  $ perl -Mwarnings -e'$SIG{WARN}=sub{}; my $a; my $b=$a+1;'
  No such signal: SIGWARN at -e line 1.
  Use of uninitialized value $a in addition (+) at -e line 1.

Re:what about catching the warnings?

mgrimes on 2010-04-08T20:18:44

Brain stopped working for a moment. Just remembered it's $SIG{__WARN__}.

Re:what about catching the warnings?

gabor on 2010-04-08T20:24:43

yeah, sorry for the bug, I was in a hurry

Re:what about catching the warnings?

mgrimes on 2010-04-08T20:38:41

I didn't mean to critique your post. I actually *just* ran into an undef warning being tossed by someone else's module. It was harmless, so I tried to use your code to catch it. Quite a coincidence.

Re:what about catching the warnings?

jleader on 2010-04-16T20:38:45

That helps, except when the warning helpfully includes the data value causing the problem, which might be different every time.

I don't get it

reezer on 2010-04-08T09:52:09

Sorry, if the following text sounds a bit rude ;-)

I don't really know what the big problem is. At the beginning you wrote you test it in a way beginners would do, meaning you don't want the modules to cause problems to beginners. Enabling use strict and use warnings is definatly the way to go in more than 99% of cases. Even Perl6 enables it per default. Nearly every module forces you to something and these two things shouldn't really be a problen in most cases. Even low level programming languages (meaning compilers) complain about a lot of things if you don'ttry to get around them.

If you really want to leave the best practice path, why not set no strict/warnings? It's not a really big problem and with a good editor/ide this can be automated. I agree that both ways have their advantages and disadventages, but it doesn't seem like a big deal to me, but that's just my opinion.

Re:I don't get it

Illusori on 2010-04-08T12:41:13

There's several problems:

One, the module is doing something that isn't its advertised function and _really_ isn't neccessary to implement its core function.

Two, it's changing behaviour of the parent program, this is a bad thing to do even when it _is_ neccessary.

Three, it isn't clear in situations like the Moose one, just when to turn it off or on, consider the following sequence within a single scope:

You enable warnings/strict. You do some code. You turn off warnings/strict. You do some code. You use Moose. You do some code. You turn on warnings/strict again not realising Moose turned them on. You do some code. You turn Moose off.

What's the state of play now? Are warnings on or off? Is strict on or off?

That depends on the implementation of how Moose is tracking warnings/strict - is it reverting to what it saw when it started, or is it counting references? It gives different results depending on how they implemented it.

This is _entirely_ uneccessary and only obsfucates behaviour that _ought_ to be under the control of the programmer.

-X

rafael on 2010-04-08T10:26:20

I fully agree. Warnings in production are a no-go. Except very specific and monitored cases maybe.

That's where "perl -X" is useful. Can one enable mod_perl running under -X yet ?

Re: I don't want to be forced into your damned war

daxim on 2010-04-08T12:12:02

How do you think about:

use warnings FATAL => 'all';

No one has commented on the necessity of empty import lists yet. How about a Critic policy that always requires an explicit import list (with configurable exceptions because the world's not perfect)? I certainly would like that.

Re: I don't want to be forced into your damned war

Alias on 2010-04-09T03:13:24

It would be nice, but a little tricky, since you need to exclude pragmas which use the import function to achieve their pragma effects.

It's doable though, and I'd happily enable that rule for Padre if someone wrote it.

Dancer favoritism?

sri on 2010-04-08T12:41:35

I have to say I was greatly impressed that the Dancer guys have offered to implement some kind of configuration option so I can explicitly turn disable their Dancer-imposed warnings in production...

This quote makes me very suspicious about the whole contest, since the Mojolicious folks never even heard a single word about this before.

Nope

xsawyerx on 2010-04-08T12:50:22

I reckon this post was instigated mainly by me. When Adam turned up on the IRC channel after he read from our posts that strict and warnings are on by default, I tried to explain it's on purpose and asked what the problem with it was.

This started a whole discussion when Adam said there's no need for it in production and I countered it saying I do want it in production.

Then the discussion flowed between the rest of the programmers on letting the user be able to change the default warnings on.

This is also something that's a bit mentioned in my recent post on blogs.perl that mentions we learned that users might want to be able to turn off the warnings.

I reckon the whole discussion (which involved a lot of experience of Adam and me on being sysadmins) pushed Adam to finally write a "No warning on production, damn it!" post.

Re:Dancer favoritism?

Aristotle on 2010-04-08T21:36:13

Good ol’ Sebastian… :-)

Damn right!

bart on 2010-04-08T13:05:55

Just like that one text editor...

I often use an idiom that I try to fetch an item, and if successful I want to do something with it; if not successful, the code returns undef (false). Typically, the code goes something like this:

if(my $item = get_item(@params)) {
    # do something with $item
    print $item->{text};
}

but this text editor complains about it, stating, in a dialog box:

a single = in a conditional is usually a typo, use == or eq to compare.
Do you want to continue?

every. single. time. I run that script.

Goddamnit, I know already. It's not a typo. Just get out of my way already, and let me do as I please.

That text editor is Padre.

Re:Damn right!

runrig on 2010-04-08T15:20:01

You'd think that the "my" would be a clue that you intended to do it that way. And a my $foo == ... in an if statement would be a red flag.

where is your bug report?

gabor on 2010-04-08T15:38:38

It is a bug. You could report it (or at least ask on the Padre channels if this is a bug or a feature).

We could tell you where to turn off the message until we fix the bug. By the way the the feature that is causing that error message is specifically designed to catch mistakes of beginners.

The idea is to catch bugs that can be caused by code like this:

if ($x = /boo/) {
}

which is valid perl but it is unlikely a beginner would know what it actually does. The example you gave probably should not trigger the error message and should be considered a bug in Padre.

Re:where is your bug report?

gabor on 2010-04-08T16:38:16

Oh and just to make a useful post as well, - after having dinner and being less grumpy - you can turn on/off the Perl beginner mode in "Edit/Preferences/Behaviour/Perl beginner mode" or "Tools/Preferences/Behaviour/Perl beginner mode" in newer versions of Padre.

Also I think I have partially fixed that specific bug in 0.59 as it was also causing test failures in Padre where we also use similar constructs here and there.

I think it is still buggy and eventually it should be implemented as a plugin for Perl::Critic to make it more roboust but that is waiting for someone to have the necessary tuits.

Almost damn right!

tsee on 2010-04-08T14:00:01

... and this is why strictures will be the default in Perl. Modules have no business mucking with your namespace, as you say. Perl does.

Note that warnings won't be the default for various reasons.

Just as a point of history

sartak on 2010-04-08T16:15:26

Moose started this whole thing, not any $Adjective::Perl modules. Moose 0.10 (released 05 Jul 2006) turns on strict and warnings for you. Modern::Perl is circa 2009.

I think it was mst who came up with the trick.

Re:Just as a point of history

chromatic on 2010-04-08T18:49:52

I concur; I saw this in Moose before I wrote M::P.

Awesome rant

patspam on 2010-04-08T21:48:54

(but -1 for the apostrophes)