Anti-Patterns Every Programmer Should Be Aware Of(sahandsaba.com)
sahandsaba.com
Anti-Patterns Every Programmer Should Be Aware Of
http://sahandsaba.com/nine-anti-patterns-every-programmer-should-be-aware-of-with-examples.html
120 comments
> I like how the first three or so anti-patterns are in the exact opposite direction as the last three or so.
These are really just proverbs, and it is not unusual to find pairs of proverbs with opposite intent - e.g. http://www.wolaver.org/WordPlay/OppositeProverbs.htm
They have some use as starting-points for discussion, but identifying when one (or which of a complementary pair) is appropriate requires good judgement, and the people who get dogmatic over these things are missing the point.
I think the fact that these sorts of argument feature so much in the discussion of software design and development indicates that there is some way to go before it becomes a fully-mature technical / engineering discipline.
These are really just proverbs, and it is not unusual to find pairs of proverbs with opposite intent - e.g. http://www.wolaver.org/WordPlay/OppositeProverbs.htm
They have some use as starting-points for discussion, but identifying when one (or which of a complementary pair) is appropriate requires good judgement, and the people who get dogmatic over these things are missing the point.
I think the fact that these sorts of argument feature so much in the discussion of software design and development indicates that there is some way to go before it becomes a fully-mature technical / engineering discipline.
"These are really just proverbs, and it is not unusual to find pairs of proverbs with opposite intent"
Or, taken to the limit, "Moderation in all things, even moderation."
(It may take a moment's thought to see how I got from here to there.)
Or, taken to the limit, "Moderation in all things, even moderation."
(It may take a moment's thought to see how I got from here to there.)
It reminds me of the quip:
"I can deliver your project quickly, cheaply and of good quality - pick two".
That said, in my experience, clients tend to see software quality as the least important attribute of the three.
The cost, time and scope trade-off - [http://4.bp.blogspot.com/_bInnDDjGM24/SlVMRBtEOEI/AAAAAAAAFu...]
I'm usually a little meaner and tell people to pick 1.7 of the above.
I'm usually a little meaner and tell people to pick 1.7 of the above.
>clients tend to see software quality as the least important attribute of the three.
It's the one attribute they can't see.
It's the one attribute they can't see.
Yeah, I think it might be better to say "quickly, cheaply, or something that can be improved on in the future without starting again", because at least that way they're all at the same level.
It's the one attribute they can't see immediately. But they will see it eventually. When fixing critical bugs take months and their customers are breathing down their necks they will see software quality at work. It's our job to educate them about these risks since they have a delayed appearance.
I don't think "educating" clients about quality is worth the effort. Just deliver code to a quality you are happy with and tell them that it takes as long as it takes (giving them feedback as regularly as you are capable of, so they get a sense of progress).
Good clients will understand and bad clients will filter themselves out quicker (which is a blessing in disguise even if they do sometimes give you money).
I prefer to let cold, hard reality do the teaching.
Good clients will understand and bad clients will filter themselves out quicker (which is a blessing in disguise even if they do sometimes give you money).
I prefer to let cold, hard reality do the teaching.
I always tell them, "You can have what you want quickly or you can have what you want cheaply, if you insist on both, you won't be getting what you want."
The cheaply part puzzles me. If it takes X hours to build something, it doesn't matter if you work D hours per day for X/D days, or D/2 hours per day for 2X/D days -- it's the same amount of hours, therefore the same amount of money. The client will pay less per day, but the same amount overall. You could charge on a curve, i.e. 8hrs/day is X, but 10hrs/day is 2X, but we all know overtime doesn't work in the long run.
It seems to me, that that saying works for only the small set of tasks that can be worked on in consistent overtime. Can someone clarify?
It seems to me, that that saying works for only the small set of tasks that can be worked on in consistent overtime. Can someone clarify?
you can cut corners by never refactoring and not thinking the design through thoroughly. In the long run this will actually cost you time but in the short run the customer gets something sooner. The problem is that the customer is usually unaware of the looming debt they are accumulating unless an engineer takes the time to explain it to them.
Though there is truth to that quip, not all the pairs of that triangle are equal. Its much easier to deliver quick and cheap, easier to deliver cheap and quality, and really hard to deliver quick and quality.
To be fair, the business has no chance of success if nothing ships before the funds run out.
> Fear of Adding Classes
I've seen this a lot, and is often made worse by attaching a load of unnecessary baggage/repurposing to classes.
For example, I've worked on PHP projects which, over time, have gained coding standards like:
- All classes should be in separate files, named to match the class name
- All classes should be built from a PHPSpec specification
- All classes should have their own test class
- No class can instantiate another, unless it's a "Manager" class which does only that. Everything else is dependency injected.
- Test classes can only instantiate their associated class; everything else must be mocked
And so on.
Now, each of these has its merits, but as each new practice was added, it increased the 'fear of adding classes', even if just subconsciously. Refactoring to break up classes became less and less common, since a simple code change would require:
- Whole new test classes (OK)
- New files (seems a bit like Java-envy, but OK)
- Injecting new instances wherever the old class was used (seems a bit overkill...)
- Mocking the behaviour of the first class in tests for the second, and vice versa (this is getting a bit silly...)
- Creating 'Manager' classes to instantiate the new classes (hmm...)
- Creating new test classes for these managers (erm...)
- Mocking the dependencies of the managers...
In the end, it was just far easier to just throw all new logic into existing classes, which grew and grew.
I've seen this a lot, and is often made worse by attaching a load of unnecessary baggage/repurposing to classes.
For example, I've worked on PHP projects which, over time, have gained coding standards like:
- All classes should be in separate files, named to match the class name
- All classes should be built from a PHPSpec specification
- All classes should have their own test class
- No class can instantiate another, unless it's a "Manager" class which does only that. Everything else is dependency injected.
- Test classes can only instantiate their associated class; everything else must be mocked
And so on.
Now, each of these has its merits, but as each new practice was added, it increased the 'fear of adding classes', even if just subconsciously. Refactoring to break up classes became less and less common, since a simple code change would require:
- Whole new test classes (OK)
- New files (seems a bit like Java-envy, but OK)
- Injecting new instances wherever the old class was used (seems a bit overkill...)
- Mocking the behaviour of the first class in tests for the second, and vice versa (this is getting a bit silly...)
- Creating 'Manager' classes to instantiate the new classes (hmm...)
- Creating new test classes for these managers (erm...)
- Mocking the dependencies of the managers...
In the end, it was just far easier to just throw all new logic into existing classes, which grew and grew.
I've worked on a codebase where objects inherited from a class named "Abstract" which contained, apparently, methods used in more than one class, regardless of purpose.
Other fun anti-pattern: make a Facade class hiding services. Then:
- add logic directly in your facade
- make the services using the facade depend themselves on the facade (circular dependencies FTW)
This an excellent way of ensuring your code will NOT be reused.
Other fun anti-pattern: make a Facade class hiding services. Then:
- add logic directly in your facade
- make the services using the facade depend themselves on the facade (circular dependencies FTW)
This an excellent way of ensuring your code will NOT be reused.
oh come on, circular dependencies are fun and you know it! ;)
I always get a bit nervous when I see Knuth's quote about premature optimisation. While the points the author makes are valid, the fact that it gets bandied about as an out-of-context sound bite results in a lot of code getting written with a complete disregard for any optimisation whatsoever.
Another far more common antipattern that isn't mentioned here is premature abstraction. You see a lot of this in enterprise .NET codebases -- such as the widespread "best practice" to build ineffective and obstructive abstraction layers "just in case you might want to swap out Entity Framework for a web service."
Another far more common antipattern that isn't mentioned here is premature abstraction. You see a lot of this in enterprise .NET codebases -- such as the widespread "best practice" to build ineffective and obstructive abstraction layers "just in case you might want to swap out Entity Framework for a web service."
I've heard about the same "ineffective and obstructive abstraction layers" in Java too and worked with the C++ code base where some development managers introduced such things, because "they're the managers, the must design with abstractions, the underlings can think about the details, and ho-ho, we certainly shouldn't be bothered to make something effective, don't prematurely optimize, everybody knows that, ha!"
So I consider it the effect of the"culture," "environment" and "organization" more than the particular language. It didn't help that the "patterns" were introduced exactly as the unstated promise to the managers to give them a "meta" approach. Like UML before etc. And that people started to think the more abstractions they force in the code, the better it becomes.
My favorite problem description is by Rico Mariani:
"The project has far too many layers of abstraction and all that nice readable code turns out to be worthless crap that never had any hope of meeting the goals much less being worth maintaining over time."
http://blogs.msdn.com/b/ricom/archive/2007/02/02/performance...
But in my opinion all big enough projects can be saved by redoing just some part of them.
So I consider it the effect of the"culture," "environment" and "organization" more than the particular language. It didn't help that the "patterns" were introduced exactly as the unstated promise to the managers to give them a "meta" approach. Like UML before etc. And that people started to think the more abstractions they force in the code, the better it becomes.
My favorite problem description is by Rico Mariani:
"The project has far too many layers of abstraction and all that nice readable code turns out to be worthless crap that never had any hope of meeting the goals much less being worth maintaining over time."
http://blogs.msdn.com/b/ricom/archive/2007/02/02/performance...
But in my opinion all big enough projects can be saved by redoing just some part of them.
The word "optimisation" should not be confused with considering computational complexity, and I believe we are in dire need for a catchy word for the latter.
One of the most important steps in programming is:
How many elements could this code actually have to process, and if there could be many: is this datastructure/algorithm suitable for that?
This is almost never premature. This should NOT be called optimisation.
I would like an own word for that. Suggestions?
One of the most important steps in programming is:
How many elements could this code actually have to process, and if there could be many: is this datastructure/algorithm suitable for that?
This is almost never premature. This should NOT be called optimisation.
I would like an own word for that. Suggestions?
Yes you should always think about optimisation, even if you never end up coding for it. Every function I write I think about what is the effect that it will have on the rest of the code and what will I need to change if it later turns out to be a bottle neck. The advantage of this is I sometimes avoid writing some dumb code - even if it never matters from a performance perspective I have thought a little bit more deeply about what I want to accomplish.
To answer your question how does thinkthencode sound?
To answer your question how does thinkthencode sound?
I think the operative word is "premature". If you actually have a reasonable idea of how much data you're going to be working with, it's not premature at that point to put some time into optimization.
Also, I would restate the rule as something like "don't spend too much time prematurely optimizing. If you're building a lookup, it's obviously a Good Thing to use a hash instead of a linked list, and in 99% of cases shouldn't take you any longer to code.
Also, I would restate the rule as something like "don't spend too much time prematurely optimizing. If you're building a lookup, it's obviously a Good Thing to use a hash instead of a linked list, and in 99% of cases shouldn't take you any longer to code.
While not particularly original, I would place this under 'thoughtfulness' / 'thoughtfully'. Code thoughtfully, but don't dive into premature optimization.
design
For the former, I always preferred Joe Armstrong's quote in Erlang and OTP in Action. “Make it work, then make it beautiful, then if you really, really have to, make it fast. 90 percent of the time, if you make it beautiful, it will already be fast. So really, just make it beautiful!”
It's the same idea as the Knuth quote, but gives you guidance over what to optimize for (rather than just leaving you with the idea you shouldn't optimize for anything). Beautiful code will oftentimes include an elegant, efficient algorithm. Many times I've found that when I've had a performance bottleneck, the fix also made the code more correct (not just on a performance standard, but things like "Oh...yeah, we totally didn't need that there" or "tweaking that to be faster also meant we just fixed a possible, if unlikely, race condition"), or more beautiful ("oh, we just removed a duplication of effort...that when called this way was turning an O(N) operation into an O(N^2) operation"); had I just adhered more stringently to those two, I would have had the performance already.
It's the same idea as the Knuth quote, but gives you guidance over what to optimize for (rather than just leaving you with the idea you shouldn't optimize for anything). Beautiful code will oftentimes include an elegant, efficient algorithm. Many times I've found that when I've had a performance bottleneck, the fix also made the code more correct (not just on a performance standard, but things like "Oh...yeah, we totally didn't need that there" or "tweaking that to be faster also meant we just fixed a possible, if unlikely, race condition"), or more beautiful ("oh, we just removed a duplication of effort...that when called this way was turning an O(N) operation into an O(N^2) operation"); had I just adhered more stringently to those two, I would have had the performance already.
> Another far more common antipattern that isn't mentioned here is premature abstraction. You see a lot of this in enterprise .NET codebases -- such as the widespread "best practice" to build ineffective and obstructive abstraction layers "just in case you might want to swap out Entity Framework for a web service."
And Java codebases
And Java codebases
Where I get twitchy is the practice of coding has changed a lot since Knuth made that quote. Optimizing code in that era typically made it brittle and hard to reason about. Pre-optimizing meant continuously painting yourself in a corner. I used to think about it as like a seamstress sewing a dress, she doesn't start out sewing nice neat tight stitches, that's for later. Once she knows she's not going to have to rip the seams apart again.
Some optimizations don't matter anymore. Back in the hoary old days pointers in C were faster than indexed arrays. Not anymore. But people still assume it's worth it. Twenty years ago, you paid a serious price for unaligned accesses. Not any more. Yet compilers still rigorously align data in memory. Which might actually make things slower.
As the follow on poster said, make the code clean and easy to reason about. People often like to think they are writing code like the big boys, where it'll get used by vast number of people. Much like a lot of hardware designers design stuff as if they are going to make millions of units. 99% of the time that's not true. Thus the NRE costs totally dominate.
Some optimizations don't matter anymore. Back in the hoary old days pointers in C were faster than indexed arrays. Not anymore. But people still assume it's worth it. Twenty years ago, you paid a serious price for unaligned accesses. Not any more. Yet compilers still rigorously align data in memory. Which might actually make things slower.
As the follow on poster said, make the code clean and easy to reason about. People often like to think they are writing code like the big boys, where it'll get used by vast number of people. Much like a lot of hardware designers design stuff as if they are going to make millions of units. 99% of the time that's not true. Thus the NRE costs totally dominate.
Yeah, I also found that making educated guesses about optimization spots early is important. You cannot not optimize all the time. Overspending resources on optimization happens because the team is not working together well or the developers aren't experienced enough. This can't be changed with a quote though. It needs time, training and good guidance by the management.
> Another far more common antipattern that isn't mentioned here is premature abstraction
He does address this one in point 9:
Useless (Poltergeist) Classes Useless classes with no real responsibility of their own, often used to just invoke methods in another class or add an unneeded layer of abstraction.
Over-abstraction is extremely common in 'Enterprise' Java codebases too.
He does address this one in point 9:
Useless (Poltergeist) Classes Useless classes with no real responsibility of their own, often used to just invoke methods in another class or add an unneeded layer of abstraction.
Over-abstraction is extremely common in 'Enterprise' Java codebases too.
"No need to worry about optimisation, modern compilers can handle that for you." /s
At least it was correctly quoted in this article, which it frequently isn't.
>While the points the author makes are valid, the fact that it gets bandied about as an out-of-context sound bite results in a lot of code getting written with a complete disregard for any optimisation whatsoever.
This is still preferable to premature optimization.
What I really can't stand is developers who tell me that this piece of code that already runs in production with no complaints is 'slow'.
And I ask them "well, did you profile it?".
"Well, no, but..."
This is still preferable to premature optimization.
What I really can't stand is developers who tell me that this piece of code that already runs in production with no complaints is 'slow'.
And I ask them "well, did you profile it?".
"Well, no, but..."
I think the article is taking it to mean what we want it to: don't write obtuse/opaque code just because you think that the straightforward way to do it, relevant language feature, natural abstraction, etc. is "too slow."
Find out whether sacrificing clarity for speed is even categorically worth it in this particular function, and find out whether your alleged speed improvement is proportional to its readability costs.
Find out whether sacrificing clarity for speed is even categorically worth it in this particular function, and find out whether your alleged speed improvement is proportional to its readability costs.
Instead of inserting abstraction layers, just design to interfaces instead.
If you're a dev and have never read these two wikipedia links, I highly recommend it.
http://en.wikipedia.org/wiki/Code_smell#Common_code_smells
http://en.wikipedia.org/wiki/Anti-pattern#Examples
http://en.wikipedia.org/wiki/Code_smell#Common_code_smells
http://en.wikipedia.org/wiki/Anti-pattern#Examples
I read A Big Ball Of Mud while in high school, left a big impression on me. Unfortunately, I've since seen that my projects all gravitate towards it at the end.
I'm pretty sure this is just the nature of the universe; increasing entropy and all that.
Those are great resources - thanks for sharing!
You forgot the most important anti-pattern of all:
"Not Invented Here".
The urge to rewrite things that you encounter is strong in software engineers, and you should always be suspicious when you find yourself thinking "I could do this so much better." Especially when it's true!
"Not Invented Here".
The urge to rewrite things that you encounter is strong in software engineers, and you should always be suspicious when you find yourself thinking "I could do this so much better." Especially when it's true!
The "not invented here" pendulum has swung a long way, and I think that there's now a real, hidden anti-pattern in industry: "never invent here".
At some point, it's important for us as programmers to indulge that "I could do this better" instinct. If you're right you could make a real contribution to your employer or the community. If you're wrong then you'll still be a better engineer (and possibly a domain expert) for the effort.
If everyone embraced "not invented here" as a philosophy then the whole industry would stagnate.
At some point, it's important for us as programmers to indulge that "I could do this better" instinct. If you're right you could make a real contribution to your employer or the community. If you're wrong then you'll still be a better engineer (and possibly a domain expert) for the effort.
If everyone embraced "not invented here" as a philosophy then the whole industry would stagnate.
Most of the time I can do it better because my problem space is so much smaller.
And sometimes I look at components and I'm so disgusted by the code or the API that I merely use it as an example and re-write the code for my own purposes. Just because somebody put it on Github or built a package doesn't automatically make the code good.
Most of the "not invented here" I come across is from developers who never look for 3rd party solutions. It doesn't even occur to them. And it's usually obvious stuff like XML or JSON parsing! Which is why the message of "Not Invented here" is a good one to repeat over and over.
And sometimes I look at components and I'm so disgusted by the code or the API that I merely use it as an example and re-write the code for my own purposes. Just because somebody put it on Github or built a package doesn't automatically make the code good.
Most of the "not invented here" I come across is from developers who never look for 3rd party solutions. It doesn't even occur to them. And it's usually obvious stuff like XML or JSON parsing! Which is why the message of "Not Invented here" is a good one to repeat over and over.
> Which is why the message of "Not Invented here" is a good one to repeat over and over.
So, there is a problem where many developers would rather work on abstractions rather than concrete value-providing things. I suffer from this myself - if abstractions weren't interesting to us, we wouldn't be working in software.
However, over the years I've discovered that many software projects are gaining negative value from some of their libraries, and I'm not sure which is the larger problem.
http://yosefk.com/blog/redundancy-vs-dependencies-which-is-w... describes the essential problem.
So, there is a problem where many developers would rather work on abstractions rather than concrete value-providing things. I suffer from this myself - if abstractions weren't interesting to us, we wouldn't be working in software.
However, over the years I've discovered that many software projects are gaining negative value from some of their libraries, and I'm not sure which is the larger problem.
http://yosefk.com/blog/redundancy-vs-dependencies-which-is-w... describes the essential problem.
So true. You always have to weight the cost of carrying around a dependency and what that will mean for others who will have to use and build your code in the future and on platforms like Windows where dependency resolution is far from trivial. Seen this way sometimes it's worth spending a few of hours writing a JSON parser to avoid adding a dependency. Probably not woth an XML parser though.
I suspect that the current patent cold war results in a number of programmers, never mind managers, being vary of glancing at third party solutions...
>obvious stuff like XML or JSON parsing!
There is one case when those wheels can be worth (partially) reinventing: if you only need them to parse some files which use XML or JSON in a very restricted way. Say you've got a bunch of XML files which only go one level deep, only use ASCII, don't use namespaces, etc. A full XML parser comes with a whole ton of bloat you don't need for that! Plus, if there's ever a problem with those files, the outsourced XML parser will tend to give the end-user absolutely useless error messages.
There is one case when those wheels can be worth (partially) reinventing: if you only need them to parse some files which use XML or JSON in a very restricted way. Say you've got a bunch of XML files which only go one level deep, only use ASCII, don't use namespaces, etc. A full XML parser comes with a whole ton of bloat you don't need for that! Plus, if there's ever a problem with those files, the outsourced XML parser will tend to give the end-user absolutely useless error messages.
This is often a slippery slope. Assumptions tend to erode over time, eg. I've seen code assuming ASCII-only input fall over due to people copy/pasting from Word (which automagically turns hyphens, quotes, etc. into non-ASCII).
Also, whenever there's a lot of data being looped over, it can be very tempting to add new features to the existing loop, rather than adding a new pass (inefficient) or trying to collate the results after they've been spread out (complicated & error-prone). This can turn a simple parser into a core piece of business logic, and of course it's then only a matter of time before custom, XML/JSON-incompatible 'directives' start creeping in to control that business logic.
Also, whenever there's a lot of data being looped over, it can be very tempting to add new features to the existing loop, rather than adding a new pass (inefficient) or trying to collate the results after they've been spread out (complicated & error-prone). This can turn a simple parser into a core piece of business logic, and of course it's then only a matter of time before custom, XML/JSON-incompatible 'directives' start creeping in to control that business logic.
I guess one cannot pick a non-controversial example. I recently had to deal with someone generating XML by string concat and it worked for years until recently. Something with international characters suddenly started causing problems. The solution was simple; I replaced the code with a proper XML library and it just worked. I didn't even bother to figure out what the underlying issue was.
Worrying about the bloat is premature optimization. If your process is actually too slow and parsing is the bottleneck then by all means rip out the XML parser library and roll your own.
Worrying about the bloat is premature optimization. If your process is actually too slow and parsing is the bottleneck then by all means rip out the XML parser library and roll your own.
I really like Spolsky's take on it:
> If it's a core business function -- do it yourself, no matter what.
http://www.joelonsoftware.com/articles/fog0000000007.html
> If it's a core business function -- do it yourself, no matter what.
http://www.joelonsoftware.com/articles/fog0000000007.html
I would say that over the past 10 years or so, especially with the rise of frameworks, PaaS and web API's, "never invent here" has become a much bigger and more troublesome anti-pattern.
Especially since the latter seems to have a much deeper and irreversible impact on the engineering culture.
Especially since the latter seems to have a much deeper and irreversible impact on the engineering culture.
Just grab 20 different containers, slapdash them together with silly string, and prey to your deity of choice that none of them hold some glaring security hole?
> At some point, it's important for us as programmers to indulge that "I could do this better" instinct.
Sure, and that point is pretty easily defined: the point at which the existing solution has produces real, tangible, definable problems with achieving your current goals is the time to invent a replacement that "does it better", with better defined in exactly the terms of the problem that you are addressing.
Its also okay to do it as something exploratory, off the critical path of a real project for understanding/skill-building.
What's not okay -- and is instead a dangerous, expensive (both in the short-term and in maintenance terms), ultimately unproductive diversion of effort -- is making a reinvention of something that works adequately a dependency for some other effort.
Sure, and that point is pretty easily defined: the point at which the existing solution has produces real, tangible, definable problems with achieving your current goals is the time to invent a replacement that "does it better", with better defined in exactly the terms of the problem that you are addressing.
Its also okay to do it as something exploratory, off the critical path of a real project for understanding/skill-building.
What's not okay -- and is instead a dangerous, expensive (both in the short-term and in maintenance terms), ultimately unproductive diversion of effort -- is making a reinvention of something that works adequately a dependency for some other effort.
And then there's that cold, dark morning when you find the profound, deeply embedded assumption at the core of a library that means you now have irreconcilable differences...
NIH is part lack of communication and understanding. Sometimes it's really hard to get the scope, qualities and failures of an external project.
NIH can be bad but it's sad that so many programmers live in the vaccum where the MVP has to be shipped tomorrow. It's great to write really good code that will out live your VC's whim.
Well people have kids all the time yet they come out more or less all the same. If you want to re-invent - go ahead and do it. Who knows, it might turn out great.
I think example 9. (the stack that wraps a linked list) is not a necessarily an antipattern. Removing degrees of freedom and adding obvious constraints make systems simpler and easier to comprehend.
Surely the purpose of the LabStack example is to restrict the interface of a LinkedList to that of a Stack? Defining a new type to present a more restricted interface is not useless... it helps one reason about what has or hasn't been done to an object.
I agree - Defining a 'Stack' class is useful even if it's just a wrapper over LinkedList, because stacks have an expected way to act, and while a LinkedList fits the bill it does more then a stack actually has to do. And with that, nothing says you have to implement a 'Stack' with a LinkedList, it's just an easy way to do it. By defining the 'Stack' class, you could always go back and improve it to not use LinkedList and instead do it yourself.
That said, I do agree with the author that, in the context of a programming class, writing the 'LabStack' class is absolutely useless because it teaches you nothing about actually using a linked-list to implement a stack, which I'm sure was the real intent of the assignment. The 'LabStack' class is useful in the context of being used in a larger program, it's not really useful for learning how to actually implement a stack because it just passes the implementation off to 'LabStack'. (That said, because Java doesn't support defining objects as value-types, using the 'LabStack' interface adds an extra level of indirection you may want to avoid - You have to access 'this.list' to get the LinkedList object, instead of just accessing it directly. This is more a fault of Java then anything else though.).
The note about programming class isn't really relevant to programming as a whole though, which is where the disconnect happens.
That said, I do agree with the author that, in the context of a programming class, writing the 'LabStack' class is absolutely useless because it teaches you nothing about actually using a linked-list to implement a stack, which I'm sure was the real intent of the assignment. The 'LabStack' class is useful in the context of being used in a larger program, it's not really useful for learning how to actually implement a stack because it just passes the implementation off to 'LabStack'. (That said, because Java doesn't support defining objects as value-types, using the 'LabStack' interface adds an extra level of indirection you may want to avoid - You have to access 'this.list' to get the LinkedList object, instead of just accessing it directly. This is more a fault of Java then anything else though.).
The note about programming class isn't really relevant to programming as a whole though, which is where the disconnect happens.
Unless the stack has to be lock free, array backed ones (ArrayDeque in java) are better on the current hardware.
I'm a little confused. I'd say that an anti-pattern is something one can avoid. Sure, I can think about every optimization I'm doing, and if I already know well enough that I'll need it and doing it right. But I can't really avoid discussing about something that is unimportant in my eyes, because the person who will discuss about it usually thinks it's important. I'd say that in the "bike shedding" regard my anti-pattern would be that I ignore some complaint as unimportant because I still don't understand it well enough.
Other things are also hard to avoid by myself, like
- God Classes (I could refactor here, but then the anti-pattern is fear of refactoring)
- Management by Numbers (I'm not a manager, what can I do about it? Actually I'm happy if my management is already that good that they have numbers. Nothing is more horrible than management who doesn't tell you explicitly what they want and then complains about details for half a year, then starts with the next project without declaring the last one to be finished or anything. I'd say numbers are good, finding the right ones is tricky, though.)
- Useless classes are avoided by simply not writing them? No, they develop when code gets refactored and nobody had looked at the responsibility of that specific class for some time. Nobody writes classes without having a goal for them in mind. Mostly calling methods of another class is fine. It happens in design patterns like Observer, Delegator, or Compositor.
Other things are also hard to avoid by myself, like
- God Classes (I could refactor here, but then the anti-pattern is fear of refactoring)
- Management by Numbers (I'm not a manager, what can I do about it? Actually I'm happy if my management is already that good that they have numbers. Nothing is more horrible than management who doesn't tell you explicitly what they want and then complains about details for half a year, then starts with the next project without declaring the last one to be finished or anything. I'd say numbers are good, finding the right ones is tricky, though.)
- Useless classes are avoided by simply not writing them? No, they develop when code gets refactored and nobody had looked at the responsibility of that specific class for some time. Nobody writes classes without having a goal for them in mind. Mostly calling methods of another class is fine. It happens in design patterns like Observer, Delegator, or Compositor.
I read this many years ago. Let me find the source.
Edit: I can't find it. Anyone else remember reading this? The mysql job queue reference was from a popular article that went out about it at the time
Edit 2: the job queue article is from 2011.
Edit 3: still can't find it. But I know every part of this article before seeing it ... I tested myself and remembered all the details... Where is this thing from?
Still can't find it. My confidence in the permanence of the web is pretty destroyed right now. I can't even find references to it existing.
Edit: I can't find it. Anyone else remember reading this? The mysql job queue reference was from a popular article that went out about it at the time
Edit 2: the job queue article is from 2011.
Edit 3: still can't find it. But I know every part of this article before seeing it ... I tested myself and remembered all the details... Where is this thing from?
Still can't find it. My confidence in the permanence of the web is pretty destroyed right now. I can't even find references to it existing.
I think you're referencing this article about MySQL job queues: https://blog.engineyard.com/2011/5-subtle-ways-youre-using-m...
Yes. This article came out after that. I realized a few paragraphs in that I had read all of it. I tested it by guessing the content and was 100% correct. But I can't find it.
This isn't the first time I wanted a searchable version of archive.org that effectively timecapsuled the interactive internet. Things fall off the net far more than common folklore suggests
Anyone got $10 mil to drop on that project?
This isn't the first time I wanted a searchable version of archive.org that effectively timecapsuled the interactive internet. Things fall off the net far more than common folklore suggests
Anyone got $10 mil to drop on that project?
I'm glad to see someone else echoing my anxiety. I had the impression that Uniform Resource Locators were meant to be treated as bookmarks into a web of knowledge, so it's like brain damage when they break. It's too bad it happens so frequently, because for a few blissful years my urge to hoard was quieted by the assumption that the internet was doing it for me.
Those enormous URLs that describe nothing but a document GUID are also a sin.
Those enormous URLs that describe nothing but a document GUID are also a sin.
I've resorted to capturing every single interesting link as a complete HTML page to DEVONthink, which is a mac application specifically meant for storing large amounts of data. It's already proven quite handy in two ways:
1. I can access articles that have disappeared off the net
2. I have a very powerful search function and 'similarity engine' constrained to only the information I've found useful so far. It's often the first place I look for certain kinds of information before I try google.
I can strongly recommend using such a tool to make sure you don't 'lose' stuff on the internet that is important to you.
I can strongly recommend using such a tool to make sure you don't 'lose' stuff on the internet that is important to you.
Anyone know of anything similar for linux or windows?
Right now i am using chrome, to save whole page as zip, but that becomes in-convenient when you have several hundreds of them.
Right now i am using chrome, to save whole page as zip, but that becomes in-convenient when you have several hundreds of them.
I made a service called http://rt2.me which tries to semantic-ify the URLs ... but I seem to be the only one that likes it.
> God Class
I don't understand how to avoid this even if you break it up in smaller classes. You still need a point of entry where the logic begins and where it is decided which components to use. ThIs always means some kind of Manager or Main class for me. How do I fix it?
I don't understand how to avoid this even if you break it up in smaller classes. You still need a point of entry where the logic begins and where it is decided which components to use. ThIs always means some kind of Manager or Main class for me. How do I fix it?
Well there's an entry point of course, but it shouldn't know much. Your entry point knows about your option parser which knows about the high-level actions your program performs which know about the sub-processes involved in accomplishing them, etc.
It's important to keep this graph as acyclic as possible, and to try not to have any one component directly interact with too many other components, because then your abstraction is too fragile.
It's important to keep this graph as acyclic as possible, and to try not to have any one component directly interact with too many other components, because then your abstraction is too fragile.
Take it all with a grain of salt.
Here's a (vaguely) contrasting view from John Carmack:
http://number-none.com/blow/john_carmack_on_inlined_code.htm...
The main takeaway is that inlining a bunch of code to flatten the dependency tree may actually reveal problems and reduce your maintenance burden.
Here's a (vaguely) contrasting view from John Carmack:
http://number-none.com/blow/john_carmack_on_inlined_code.htm...
The main takeaway is that inlining a bunch of code to flatten the dependency tree may actually reveal problems and reduce your maintenance burden.
Enjoyed this list. Over the years I've become better and better at spotting these issues, but even still sometimes it's hard to see them creeping up on you.
The worst for me these days is analysis paralysis. I'm currently working on my own so I don't have anyone around to bounce ideas off. For smaller tasks it's not a problem, but there are bigger design decisions that have ended up taking longer than probably should. When you're left to figure them out on your own it takes a lot longer to convince yourself of the "better" way of doing something.
The worst for me these days is analysis paralysis. I'm currently working on my own so I don't have anyone around to bounce ideas off. For smaller tasks it's not a problem, but there are bigger design decisions that have ended up taking longer than probably should. When you're left to figure them out on your own it takes a lot longer to convince yourself of the "better" way of doing something.
To me, the problem in choosing the "better" way of doing something is that I can't stop comparing diverging design choices.
I like to think that the best way to choose between seemingly equally advantageous designs is to start with the one whose first step(s) is(are) the most straightforward. The thing is, while I prepare myself for implementation of that first step, I've a background loop in my mind that constantly checks against other implementations choices. What I am losing here, what I would gain otherwise.
In the end, I never really make definitive decision before starting. I start with that background brain noise on the “most simple first step design”, and when the background noise stops and the raw pleasure of coding kicks in, I known I'm on a good track.
I like to think that the best way to choose between seemingly equally advantageous designs is to start with the one whose first step(s) is(are) the most straightforward. The thing is, while I prepare myself for implementation of that first step, I've a background loop in my mind that constantly checks against other implementations choices. What I am losing here, what I would gain otherwise.
In the end, I never really make definitive decision before starting. I start with that background brain noise on the “most simple first step design”, and when the background noise stops and the raw pleasure of coding kicks in, I known I'm on a good track.
I guess I do go through that process once I start work, but I'll definitely keep your advice in the back of my mind. Don't let the choice become overwhelming, get started knowing that it's ok to change your mind during the process.
For any feature / bit of work that's in isolation, I don't really worry. I figure out an approach and I implement it. When it comes to changing data structures / larger changes within the product there's a fear of getting it wrong and having a mess to unpick later.
For any feature / bit of work that's in isolation, I don't really worry. I figure out an approach and I implement it. When it comes to changing data structures / larger changes within the product there's a fear of getting it wrong and having a mess to unpick later.
"It seems that perfection is attained, not when there is nothing more to add, but when there is nothing more to take away."
Great quote - It reminds me of the story of when Michelangelo unveiled the statue of David, someone asked him how he managed to create such a masterpiece and Michelangelo answered "It was simple, I just chipped away all the rock that wasn't David".
Great quote - It reminds me of the story of when Michelangelo unveiled the statue of David, someone asked him how he managed to create such a masterpiece and Michelangelo answered "It was simple, I just chipped away all the rock that wasn't David".
"To clarify, add detail" - E. Tufte
I don't think reduction only leads to perfection. If it would be, then our starting point must always be the right one. But in practice you start somewhere, reduce, move forward, add stuff, move sideways, try out and test. Sometimes you need to add stuff to make it clearer to your users.
I don't think reduction only leads to perfection. If it would be, then our starting point must always be the right one. But in practice you start somewhere, reduce, move forward, add stuff, move sideways, try out and test. Sometimes you need to add stuff to make it clearer to your users.
Is https://en.wikipedia.org/wiki/Facade_pattern a God class? I like the idea if it only delegates to other classes. Its downside is the dependencies to many classes.
#5 is a classic example of the Expression Problem[1]. Are you trying to add more shapes or more functions that operate on shapes?
http://c2.com/cgi/wiki?ExpressionProblem
http://c2.com/cgi/wiki?ExpressionProblem
Having worked for big corporations in Europe, I'd like to see manager types spend 10% of time technical people spend on self-optimising themselves, their working environment, post-mortems, understanding antipatterns, productivity etc.
Most of the evil done in my company seems to be by Ctrl+C Ctrl+V and then changing 1 line of code.
Same here, unfortunately. It drives me nuts. It's oh so easy and tempting to vialoate the DRY principle. But what's even worse is people actually defending their Ctrl+C Ctrl+V behaviour with false/uninformed arguments such as
- why waste time with creating a function when I can copy-paste?
- but I'm never going to have to change this code anymore
- but it's more readable else I have to figure out what the function will do, browse to it, ... (well, good luck navigating any codebase at all)
- but it occurs only twice (ok, twice is debatable, but still: before you know it the same is used again in another place an there we go again..)
- but it's faster because it doesn't require an actual function call (this is by far the worst one: they claim this without having even measured it, without having checked if there even is a bottleneck, without realizing that in compiled laguages any decent optimizer would inline and generate assembly anyway)
- why waste time with creating a function when I can copy-paste?
- but I'm never going to have to change this code anymore
- but it's more readable else I have to figure out what the function will do, browse to it, ... (well, good luck navigating any codebase at all)
- but it occurs only twice (ok, twice is debatable, but still: before you know it the same is used again in another place an there we go again..)
- but it's faster because it doesn't require an actual function call (this is by far the worst one: they claim this without having even measured it, without having checked if there even is a bottleneck, without realizing that in compiled laguages any decent optimizer would inline and generate assembly anyway)
Half afraid to read this, I think they might be our programming conventions at work.
It's nice to notice that top three actually does not have anything to do with programming. Those are absolutely general issues when ever doing anything at all.
Isn't it blasphemy to quote Greenspun's tenth rule right after insisting in two consecutive rules that people create enough classes?
No, because CLOS is awesome.
> "Magic Numbers and Strings"
I don't know about this one...we need something have a chuckle (or cry) about over beer after work, and to make sure software archaeologists in 100 years time have an interesting job (not to mention plenty of opportunities to write blog-spam)!
I don't know about this one...we need something have a chuckle (or cry) about over beer after work, and to make sure software archaeologists in 100 years time have an interesting job (not to mention plenty of opportunities to write blog-spam)!
It's not bad advice, if used in moderation. The problem is that most developers take it too far, to the point where no number is safe from becoming a named constant.
My default these days is to write numbers as numbers, plus maybe a comment if things are unclear. I only turn numbers and strings into named constants if they are used in more than one place, or if they are true configuration values that need to be tweaked.
This makes the program easier to read (the ultimate goal) by avoiding the need to jump around in the codebase. Comments are better at explaining things than cryptic variable names anyhow.
My default these days is to write numbers as numbers, plus maybe a comment if things are unclear. I only turn numbers and strings into named constants if they are used in more than one place, or if they are true configuration values that need to be tweaked.
This makes the program easier to read (the ultimate goal) by avoiding the need to jump around in the codebase. Comments are better at explaining things than cryptic variable names anyhow.
Definitely, although there can also be refactoring benefits to look out for. In the article example it would be much easier to change the default popup window size (800x800) across the project if they're all using the same variable.
On the other hand, tools like CheckStyle can make people do some strange things when they take it too literally.
I once reviewed code that defined: static final int
On the other hand, tools like CheckStyle can make people do some strange things when they take it too literally.
I once reviewed code that defined: static final int
Right, but your window size example would be a case of using the same value in more than one place, so it would be better as a named constant.
Actually, the whole example smells ripe for refactoring. Where are there multiple windows throughout the app all sharing a default starting size? Sounds like it's time for a base class or something. Once you've eliminated the redundant sizing code, you will find that your default size values now exist in only one place (the base class). Since they are only in one place, you can write them as numbers again instead of named constants.
I've played this pattern out more times than I can count. The problem isn't magic values sprinkled through the code; it's having redundant code in the first place. Once you solve that, the magic numbers tend to evaporate.
Actually, the whole example smells ripe for refactoring. Where are there multiple windows throughout the app all sharing a default starting size? Sounds like it's time for a base class or something. Once you've eliminated the redundant sizing code, you will find that your default size values now exist in only one place (the base class). Since they are only in one place, you can write them as numbers again instead of named constants.
I've played this pattern out more times than I can count. The problem isn't magic values sprinkled through the code; it's having redundant code in the first place. Once you solve that, the magic numbers tend to evaporate.
Could have been worse:
static final int // Changed to 50 to optimizeI'm still laughing.
And it's funny because it's true.
And ... the funny could have been eliminated if the named constant had just had a useful name, or been replaced by a function, rather than a passive-aggressive response to a code analyzer or coding standards ("There, I fixed it."). The conflict between the name and its value would have been eliminated, and the comment may have never been written in the first place.
And it's funny because it's true.
And ... the funny could have been eliminated if the named constant had just had a useful name, or been replaced by a function, rather than a passive-aggressive response to a code analyzer or coding standards ("There, I fixed it."). The conflict between the name and its value would have been eliminated, and the comment may have never been written in the first place.
Lol - I have to say that I have seen this sort of thing a few times in the past. I actually don't mind this as it is a flashing red light that the code that uses this constant needs to be looked at very, very carefully and rewritten.
> I once reviewed code that defined: static final int>I saw:
static final int>a while back, which really made me laugh.
static final int>a while back, which really made me laugh.
I've mentioned this on HN before, but I remember seeing some code years ago that had:
static final String HTTP = "http";
static final String COLON = ":";
static final String SLASH = "/";
String url = HTTP + COLON + SLASH + SLASH + ....;
Which I guess isn't wrong, but isn't right either :-)Maybe 100 is a not-so-famous mathematical constant named after John One Hundred. Who knows?
> Comments are better at explaining things than cryptic variable names anyhow.
I'd pick
Two reasons: version 2 encourages others to copy-paste out of laziness, and it does not resist refactoring very well.
I'd pick
const DEFAULT_FONT_SIZE = 42;
// ... lots of code in the same file
var font = new Font(DEFAULT_FONT_SIZE)
over // 42 is the default font size
var font = new Font(42);
any time.Two reasons: version 2 encourages others to copy-paste out of laziness, and it does not resist refactoring very well.
[deleted]
That one stuck out to me as well, but for a different reason.
I think the idea that "explicit is better than implicit" is a good idea for more than simply "magic numbers and strings".
I much prefer seeing the code in front of me than having to jump through multiple functions across several source files + tracking down the configuration values I'm looking for because someone decided to use DI.
I'm not saying DI is a bad practice, but unless you really need it, prefer explicit dependencies.
And on and on with just about everything you do, if all things are equal, prefer explicit over implicit.
I think the idea that "explicit is better than implicit" is a good idea for more than simply "magic numbers and strings".
I much prefer seeing the code in front of me than having to jump through multiple functions across several source files + tracking down the configuration values I'm looking for because someone decided to use DI.
I'm not saying DI is a bad practice, but unless you really need it, prefer explicit dependencies.
And on and on with just about everything you do, if all things are equal, prefer explicit over implicit.
for
" Writing a task scheduler for your web-server in PHP"
Is the author talking about "at" and "cron" instead ?
" Writing a task scheduler for your web-server in PHP"
Is the author talking about "at" and "cron" instead ?
What are good queue replacements? My problem with activemq et. al. style queues is that monitoring them is much harder than a simple database. Ie. checking for pending items in the queue and also for debuggin processed items later. And somehow I don't "trust" these queues as much as a simple database. If I write a row to the database it is there and can easily be checked and used (no queue product has any comman line utilities or sql like general functionality to check the queue etc.)
The previously linked article also warns that do not use a database as the backend for the queue solution or you will have worst of both worlds...
There's a number of solutions, depending on your stack: at and cron are good OS level solutions. Sidekiq is great for Ruby.
I think the problem lies in that PHP wasn't designed to be a long running process, and for a scheduler you need something long running.
I think the problem lies in that PHP wasn't designed to be a long running process, and for a scheduler you need something long running.
Having done this (years ago), there are good reasons sometimes. At the time I was working on a relatively complex hotel reservation system with a distributed database and the need for batch execution of background processes (things like sending emails and digital fax from a queue).
The master interface to the database was a series of PHP functions since the data massaging that had to occur (to facilitate correct interaction in multiple human languages across disparate timezones versus user preferences and time of day, etc.) ruled out direct DB access from anywhere else, back-end processes included.
I think the end solution was something like 'every minute run PHP X from cron, which checks if there are jobs, if so successively spawn children to handle'. It was basic but it worked. Godawful pain with MySQL replication over lossy Chinese internet WAN ... never again.
The master interface to the database was a series of PHP functions since the data massaging that had to occur (to facilitate correct interaction in multiple human languages across disparate timezones versus user preferences and time of day, etc.) ruled out direct DB access from anywhere else, back-end processes included.
I think the end solution was something like 'every minute run PHP X from cron, which checks if there are jobs, if so successively spawn children to handle'. It was basic but it worked. Godawful pain with MySQL replication over lossy Chinese internet WAN ... never again.
I inherited a similiar system. It was painful to say the least. I didn't realise how painful until I started using proper queueing systems and Sidekiq.
Sidekiq doesn't really look that different to what we used.
Anyway, neither decent ruby unicode handling nor RoR nor sidekiq existed back then. I actually considered rewriting the whole system in ruby at one point, but the unicode support was still dodgy.
Anyway, neither decent ruby unicode handling nor RoR nor sidekiq existed back then. I actually considered rewriting the whole system in ruby at one point, but the unicode support was still dodgy.
cron and a queue server are fundamentally different systems.
And it's exactly this requirement of masterful manipulation of balance that makes me love this field so much and long to get better at it. Getting a handle on the artsy spirit of programming is really what separates the wheat from the chaff in terms of programmer skill. There is no formal programmer guild in real life, but if there was, and there was some sort of test a journey-man programmer must undergo in order to become a master programmer, it would be the test of being given a large project and then deciding correctly exactly how much technical debt to take on to be able to ship a product within a reasonable time-frame and yet have its internals not be complete unmanageable spaghetti.