35 comments

[ 2.9 ms ] story [ 91.6 ms ] thread
I don't think there's any disagreement about using the right tool for the job. What I took from the original MicroPHP Manifesto is that the solution is not to have a ton of hammers to choose from. Instead, make sure you have a couple different types of hammer heads and a couple different types of handles that you can mix and match so that the right hammer is always available.

This way your toolbox is much lighter and easier to carry around (easier to keep up to date with improvements to the code and easier to maintain). I think it's about breaking things into smaller pieces.

I used to feel this way. However I tended to find myself, on most projects, spending a surprising amount of time tweaking incompatibilities between the various lightweight libraries I was using. And it was a bit of an effort to unify things like logging and error reporting.

Which is when I realised the utility of a bulky framework; for the most part it is a suite of libraries that work together/are compatible but which someone else maintains for me :)

This was a big step for me.

(although I still use the lightweight approach for smaller projects)

That's a reasonable complaint, but maybe it means we need better lightweight libraries with well thought out interfaces or better library management like deweller suggested in the comments on the original thread.
That would be good - but then you begin to talk about a unified logging interface and standardisation like folder structure, class loading, class interfaces and so on.

At which point it's basically a framework (perhaps without the routing/initialisation code).

One thing I would like to see (no idea if it exists) is a core package architecture (that did all of the above) and let you hang packages as and when you need them.

I've been keeping an eye out for such a thing for a while with no joy.

I'm excited to see that there is discussion going on here! I might suggest that the "manifesto" author did not say "I'm a PHP developer" to be limiting (i.e. I only write PHP) but to contrast with being e.g. a "Cake developer" or a "Rails developer." Assuming this is a reasonable interpretation, he is actually making the same "Don't tie yourself to one framework" point that OP is making here. So: agreement! (I think.)

I don't know how relevant this is but I couldn't help but notice that the "hammer" analogy for frameworks was an interesting choice in light of Joel S.'s well known "Hammer Factory Factory" article. http://discuss.joelonsoftware.com/default.asp?joel.3.219431.... Was this a coincidence or intentional? Anyway I thought it was a funny coincidence. :)

Yeah, by being a "PHP developer" rather than say, a Zend developer, he was making that right-hammer-for-the-job argument back in the original article long before ircmaxell got around to implying anything else. I think there's a lot of agreement here that got lost in "simple isn't always better!"
Looking at it now I see you're right. "PHP developer" is meant to be less limiting than the options the author of the MicroPHP Manifesto was considering.

When it was brought up in the context of this article, I read it as someone being proud to be a developer of PHP. Among many academic crowds and HN it feels like PHP is a dirty word.

His last bit on the latter part of the manifesto "I like simple, readable code" seems pretty unnecessary. I doubt the original article's author would argue against good coding practices like abstraction layers. And really, the example he provides doesn't even stand against "simple" or "readable". Abstracted code is exactly that.
You know what "simple, readable code" is? It's something that hasn't been exposed to the real world.

Once your application has been in circulation, exposed to the elements, hostile or indifferent users, it will develop barnacle-like patches that look ugly but serve a specific and important purpose.

You can keep code clean(er) if you're vigilant, but sometimes there's no way to express very complicated logic in a concise manner. Often abstraction looks cleaner but generally only hides complexity and can tend to increase complexity on the whole.

As a noob I find terms "simple, readable code" really confusing. I recently had to learn some PHP (I'm mostly Python) to implement a seemingly stupid simple interface between some HTML form input and a REST API. Should be simple, right?

But there is all this weirdness validating data inputs like dates. And we have to process the form data data so each field input goes to the proper API field. And hey look that mapping only varies in its _data_ among form / API target pairings, so I should abstract that code out into a function or I'll be chasing bugs through the copied-and-pasted. The API provides a library for formulating requests but this doesn't expose some key functionalities to unit testing so I ant to revise to do that. Et cetera.

And, the next thing I know this thing is umpty-ump lines, and while the scripts actually processing the data are reasonably legible, they're sitting over a tangle of stuff that's rather harder to navigate. I've got classes to manipulate various core data to produce various data structures supporting sundry activities, but this essentially means those data structures are abstractions and thus hard to inspect when trying to remember how some process is working or what's going wrong. (The unit tests are worthwhile if only b/c their check values provide concrete examples of how those derived structures look.) To make the code "simple" in one place, I've created complexity elsewhere.

Obviously, this is in part a novice problem. If I can lay out and document the supporting code better everything gets easier to follow. But still, 5000 lines to input some form data, am I doing this right?

So is "simple, readable code" that helpful as a goal? At the end of the day it seems like there is X degree of complexity in any of these tasks, and any strategy to "simplify" is really just a framework for deciding where to place what complexities. And if that's right, then "simple, readable code" can become either a time suck or a discouragement in a hurry. I'm either playing Whack-A-Mole as I chase Complexity from one corner to the next and back to the start, or I'm going home depressed b/c I'm too stupid to write "simple, readable code". It sounds great, I suppose it is where people end up when they get good, so it's probably very helpful as an indicator of whether you're good yet. But I'm not sure it's very helpful for figuring out how to get to good.

I'd suggest you read Code Complete. It clocks in around 1000 pages and you probably don't have to read every single corner, but it would be for the best.

Code Complete documents several best practices in coding based on internal reviews of large coding projects and publications. The point of the book is that your code will have fewer bugs and be easier to maintain.

Priorities for coding go something like this:

1. make it work

2. optimize for maintenance (ie, make it easy to read)

3. optimize for flexibility/speed (only if necessary!!!!)

You should not be chasing complexity from one corner to the next; complexity should nearly always be pushed down and sequestered where it can't infect other code. Simple, readable code is the primary goal. Perhaps it was Knuth? who said programmers should enjoy reading code on the weekend. I couldn't disagree more! Good code should have the opposite qualities of a good book. Every single step along the way should be perfectly obvious as if no other way existed; plot twists in code are bad! Elegance is good, while cleverness tends to be bad. Working on existing code is much harder than writing new code. What hope do you have with maintenance if you write something at the limits of your comprehension?

About 70-80% of the lifetime of any work on given code is actually in maintenance, not in writing it. Optimize for this. Documenting is not necessarily helpful, either. You should not document shit as being shit; instead, you should change the bad code!

Other things are mentioned too, like function length, number of arguments to functions, variable span length, and variable pass through. Really, give it a read.

Code Complete is very helpful, read it several years ago when I was starting. The biggest take-aways were DRY and proper encapsulation / abstraction. I should probably go back to it, I currently struggle with figuring out where to put stuff but maybe the stuff that went over my head the first time would help with that.

thx

You don't sound like a noob to me. It's important to just get some shit working sometimes. Clean ans readable is a nice goal to have. In fact there are a ton of things "good" coders do that we'd all like to strive for but in the end I always ask myself "is following x principle getting shit working?" and if the answer isn't a clear yes then it doesn't matter. We need to stop getting hung up on all this stuff. Just make something work. Try to be clean, concise, and readable but make sure the motherfucker works at the end of the day or it's a waste.
Well designed and well organized might be a better goal. As you suggest, there does is some bound on the minimum amount of complexity inherent in the problem. The goal is to reduce the unnecessary complexity in your code. Being able to recognize and remove unnecessary complexity is difficult.
If using an example involving Dave Grohl can't set this straight, nothing will.
But where does Rick Allen fit in here? I must know!!
> Programming in a language involves limiting thoughts and implementations to only constructs that the language directly supports. However, programming into a language involves finding the solution they want to do, and then determining how to express those thoughts in the language.

Learning how to program involves learning to think like a computer, and seeing the world in terms of loops and filters and manipulations. Mastering a specific programming language takes that one step further, because you become so familiar with it that you start thinking in terms of the specific constructs and the idiom that it offers. The speed gains are enormous once your brain adapts to that way of thinking, and I wouldn't give that up for anything.

I appreciate the sentiment – be a programmer, not the master of one specific way of programming – but I think there are better ways to achieve that goal than by programming "into" a language. The Pragmatic Programmer credo, for example, to learn a new language ever year, is much more appealing.

This article wasn't really necessary. Okay, so the guy who wrote the MicroPHP Manifesto didn't use a perfect analogy. So what? I'm pretty sure we all got his point. Everything that got refuted here in this post didn't need to be refuted. We're all smart enough to understand that the MicroPHP Manifesto was a reaction to large, sometimes complicated frameworks that caused the author frustration. All he was saying was that he just wants to use less code and simplify things. Who can argue with that? Even a novice developer would understand that his manifesto wasn't the one true way. It's just something to think about before diving into Zend or Cake by default.

Why nitpick? There's always more to what you read than just the words on screen. Considering the motivation and circumstances behind the MicroPHP Manifesto I can't see why anyone would make it out to be something it isn't, bad analogies or not.

I believe it was both necessary and not a nitpick. While I agree choosing Cake or Zend by default is a fool's errand, the MicroPHP Manifesto read like someone who'd just found CodeIgniter for the first time.
I suggest you do some research about the author of the MicroPHP manifesto before throwing snark out there like that.

Ed is a personal friend (and we do a podcast together) with many, MANY years of experience building applications using PHP. While I don't agree with him on some points in there, he does deserve to not be shit on by people who can't be bothered to do 30 seconds of research about the author.

I didn't "shit on him", I criticized his tenor in that article.
I didn't do any research about the author and just now found out his name is Ed. I mention this because I'm someone whose only experience with this author was that one post and even with that small amount of information I was able to infer that the guy had experience and knew what he was talking about. I also think its very obvious that what Ed suggests is not to be taken at face value. When I read the microPHP Manifesto my reaction was that it made total sense but of course, as always, follow it if and when it makes sense. Taking into account that the post was probably a kind of rant as a reaction to some framework frustrations his manifesto makes perfect sense.

Now the author of this article refuting the Manifesto seems to be under the impression that what Ed suggests should be the way things are done from now on. It's clear as day that all Ed was sayng was that sometimes the MicroPHP Manifesto tenets make more sense. And they do! Sometimes!

Guess what? Everyone is right! Hopefully that doesn't make some people's brain explode but both authors are correct. Mileage varies. The thing I didn't like most about this was how he picked apart the drummer analogy. I felt like it was a low blow and not really necessary.

This perfectly encapsulates my unease about the MicroPHP Manifesto. Neither drummer was doing it wrong; they both chose the most fitting tools. The reviling of "overkill" solutions is a natural step in becoming an experienced developer but that doesn't mean it should top HN every time someone has that epiphany.
Of the two methods he shows, I want to cast a strong dissenting vote that I prefer method number 1. In method 1, the complexity of the operation -- and it's not that much complexity, let's be honest here! -- is all in one place where I can examine and work with it, but there's not so much that it's a barrier to understanding.

By contrast, in method 2, the complexity is hidden. If there's a bug in method 2, I have way more levels of abstraction to dig through before I've got a clue what's going on. If someone else wrote method 2, I've got probably three or four source files to do archaeology in before I can even be sure what it does.

If that's all that your code does, then that's fine to use #1. But in a larger class, it's needlessly cluttering the class with operations below its abstraction level. Why should a logger care about file locks and the such? Why should you care about file locks when editing the logger? Hint: You shouldn't normally.

As far as "the complexity is hidden", you're absolutely right. I want that complexity hidden. By hiding the complexity in this way, I can reduce duplication and at the same time make the code far easier to read. Sure, you do need to dig through more levels of abstraction if you need to debug something. But abstracting in this way enables bugs to be fixed far easier, since the methods are really small and simple, the "ripple" effect is far easier to understand and contain.

Think about how long it took you to understand what log() did. With the first one, you needed to parse a whole lot of detail (including the flag passed to fopen, the two exception checks, the arguments passed to flock, the fprintf declaration and the flock call arguments). With the second, all you needed to do is read the two steps: 1. createLogMessage() and 2. file->append($message). At a glance you know what the method is supposed to be doing.

Why is there such a hang up that people want to know what code is doing at all levels? If you name your APIs well, you should be able to look at the method's name (and perhaps its arguments in some cases) and know without a doubt what it's doing (at least to the abstraction level the API is designed for). If you really need to know details, you can go deeper, but I know when I read $file->append($message) that I'm appending a message to a file. I don't need to worry about anything else 99.9% of the time. So I'd rather get the clean win with well named APIs, than spend my time sifting through methods like the first one...

If your abstractions are beautiful and clean, then you're absolutely right -- hiding the complexity is great! But you know what? 99% of the time, your abstractions are shitty and leaky. And then I need to know what's under them in order to work with them confidently.

So yes, absolutely hide complexity. But hide it judiciously; hide it only when you know you have a clean, well-designed and well-constructed abstraction that won't leak to the outside. But if you hide complexity behind a leaky abstraction, which most of them are; then now, as they say, you have two problems.

I feel the concept of "leaky abstractions" is being abused way too much as an argument against all abstraction.

Except in the well known cases, like trying to abstract away SQL to give it an OO interface, the leakiness of abstractions is rarely a big issue if those abstractions are decently designed. Not even beautiful and perfectly clean, just good enough for their purpose.

Yeah, it sucks in those rare cases where the abstraction makes it hard to figure out what the hell is actually happening, but the extra effort is nothing compared to the amount of pain saved by having that abstraction throughout the rest of the development process.

All plumbing will spring a leak some time (and sometimes with pretty costly consequences), but that in itself is no argument against plumbing.

The code example given seems a bit disingenuous, since the same level of complexity is present in the code, only the parts of it are broken up into single-purpose methods that may or may not be overkill depending on the needs of the project. For example, you can swap `$this->file` with a different object to write to alternate logging backends, but you could just as easily replace `$this->logFile` with an alternate stream wrapper to do the same thing in the original example...

For reference, here's the first example:

    public function log($message, $level) {
        $f = fopen($this->logFile, 'a');
        if (!$f) {
            throw new LogicException('Could not open file for writing');
        }
        if (!flock($f, LOCK_EX | LOCK_NB)) {
            throw new RuntimeException('Could not lock file');
        }
        fprintf($f, '%s [%s] %s - %s', $this->machineName, date('Y-m-d H:i:s'), $level, $message);
        flock($f, LOCK_UN);
        fclose($f);
    }
And here's what your improved example would actually be:

    /* In $this->file's class */
    public function append ($message) {
        $f = fopen($this->logFile, 'a');
        if (!$f) {
            throw new LogicException('Could not open file for writing');
        }
        if (!flock($f, LOCK_EX | LOCK_NB)) {
            throw new RuntimeException('Could not lock file');
        }
        fwrite($f, $message);
        flock($f, LOCK_UN);
        fclose($f);
    }
    
    /* In Log class */
    public function createLogMessage ($message, $level) {
        return sprintf('%s [%s] %s - %s', $this->machineName, date('Y-m-d H:i:s'), $level, $message);
    }
    
    public function log($message, $level = 'notice') {
        $message = $this->createLogMessage($message, $level);
        $this->file->append($message);
    }
Just a nitpick, but sometimes we over-abstract because we're too used to doing so. To me, a separate `createLogMessage()` method that just wraps a `sprintf()` seems like a sign of that.

(not saying I haven't been equally guilty of this 1000x before :)

If you're going to be creating log messages all over the place, that createLogMessage method is actually really useful. Not only does it enforce having the machine name and date (and date in a certain format), but calling it saves looking up what the argument to date has to be to get the format you want (which I nearly always have to do). It seems like an excellent case of hiding the fiddly details.
Still it doesn't necessarily do much more than the hard-coded "fprintf($f, '%s [%s] %s - %s', $this->machineName, date('Y-m-d H:i:s'), $level, $message);" already does in the original, unless you want to override it separately.

I'd probably do something more like this:

  public $format = '%s [%s] %s - %s'; // machine, date, level, message

  public function formatMessage ($level, $message) {
    return sprintf ($this->format, $this->machine, date('Y-m-d H:i:s'), $level, $message);
  }

  public function log ($level, $message) {
    return $this->write ($this->formatMessage ($level, $message));
  }
That way you could change the format with no subclassing needed:

  $logger->format = '%s, %s, %s, %s';
I also like the idea of using a stream wrapper to override the logFile property:

  $logger->logFile = 'db://user:pass@localhost';
So again instead of subclassing, you just implement a stream wrapper as shown here:

http://www.php.net/manual/en/stream.streamwrapper.example-1....

Perhaps that's just my personal aversion to needless depth in class hierarchy talking though... :)

Edit: Wow does this thread have me geeking out! Haha, cheers :)

I'm intrigued by the stream wrapper idea, but I don't think your "change the format" is general enough... sure, you can change the format, as long as you change it to something that uses the other arguments sprintf is being passed, but that's a pretty strong restriction. Maybe it would be workable if you had log() take optional argument(s) that represent the args to sprintf, too, but I'm not sure that it's worth it.
True, and using %1$s style references in the format in order to change their ordering is a lot uglier than overriding a method. In my last example, I did provide a formatMessage() which was the equivalent of the createLogMessage() for purposes of overriding. Best of both worlds :)
The OP doesn't include the implementation of "createLogMessage" because he is suggesting that this method would be part of a framework, and by using it you can focus your mind on your application code, rather than boring details like checking for file locks on a log file.

I do think that choosing where to abstract and where to split functionality is the "art" of the code and we obsess about as we master the craft. So I can appreciate your code example!

I personally like to have a dedicated log function. That way you can refactor from using a file to perhaps a database table or any other method of logging. I personally expose our error logs as RSS and subscribe to the feed - I find it a really easy way to monitor because I'm too lazy to go looking at error log files every day!

I was assuming the log() method was also part of the framework, and that this was just a framework-level excerpt... ;)

Perhaps such a core Log class could use a separate createLogMessage() method (or formatMessage() as I'd probably name it) that could be overridden in a subclass more easily than overriding the entire log() method.

I definitely agree that the API design and finding that balance is a big part of what makes coding an artform!