Positronic VibrationsMaking the ugly elegant one day at a time
|
My Stuff Front Page Presentations Projects Music Favorite Quotes Respect Vincent Foley-Bourgon Sam Griffith LeRoy Mattingly Colin Putney Matt Secoske Sam Tesla Andres Valloud Admiration Leo Brodie Avi Bryant Alan Cooper Steve Dekorte Stephane Ducasse Doug Engelbart Eric Evans Brian Foote Martin Fowler Paul Graham Dan Ingalls Alan Kay John McCarthy Steve McConnell Peter Norvig Niall Ross Randall Smith Gerald Jay Sussman David Ungar Rebecca Wirfs-Brock ...And So Many More... My Amps Squeak JavaScript Scheme Java Corman Lisp Ruby Dolphin Smalltalk Cincom Smalltalk Self Archives 05/01/2003 - 06/01/2003 06/01/2003 - 07/01/2003 07/01/2003 - 08/01/2003 08/01/2003 - 09/01/2003 09/01/2003 - 10/01/2003 10/01/2003 - 11/01/2003 11/01/2003 - 12/01/2003 12/01/2003 - 01/01/2004 01/01/2004 - 02/01/2004 02/01/2004 - 03/01/2004 03/01/2004 - 04/01/2004 04/01/2004 - 05/01/2004 05/01/2004 - 06/01/2004 06/01/2004 - 07/01/2004 07/01/2004 - 08/01/2004 08/01/2004 - 09/01/2004 09/01/2004 - 10/01/2004 10/01/2004 - 11/01/2004 11/01/2004 - 12/01/2004 12/01/2004 - 01/01/2005 01/01/2005 - 02/01/2005 02/01/2005 - 03/01/2005 03/01/2005 - 04/01/2005 04/01/2005 - 05/01/2005 05/01/2005 - 06/01/2005 06/01/2005 - 07/01/2005 07/01/2005 - 08/01/2005 08/01/2005 - 09/01/2005 09/01/2005 - 10/01/2005 10/01/2005 - 11/01/2005 11/01/2005 - 12/01/2005 12/01/2005 - 01/01/2006 01/01/2006 - 02/01/2006 02/01/2006 - 03/01/2006 03/01/2006 - 04/01/2006 04/01/2006 - 05/01/2006 05/01/2006 - 06/01/2006 06/01/2006 - 07/01/2006 07/01/2006 - 08/01/2006 08/01/2006 - 09/01/2006 09/01/2006 - 10/01/2006 10/01/2006 - 11/01/2006 11/01/2006 - 12/01/2006 12/01/2006 - 01/01/2007 01/01/2007 - 02/01/2007 02/01/2007 - 03/01/2007 03/01/2007 - 04/01/2007 04/01/2007 - 05/01/2007 05/01/2007 - 06/01/2007 06/01/2007 - 07/01/2007 07/01/2007 - 08/01/2007 08/01/2007 - 09/01/2007 09/01/2007 - 10/01/2007 10/01/2007 - 11/01/2007 11/01/2007 - 12/01/2007 12/01/2007 - 01/01/2008 01/01/2008 - 02/01/2008 02/01/2008 - 03/01/2008 03/01/2008 - 04/01/2008 04/01/2008 - 05/01/2008 05/01/2008 - 06/01/2008 06/01/2008 - 07/01/2008 07/01/2008 - 08/01/2008 08/01/2008 - 09/01/2008 10/01/2008 - 11/01/2008 01/01/2009 - 02/01/2009 09/01/2009 - 10/01/2009 10/01/2009 - 11/01/2009 Feed ![]()
|
Saturday, October 17, 2009Devilishly Clever
I was doing my usual research on Python when I ran across this recipe to make tail recursive calls not blow the stack in Python: Tail Call Optimization Decorator. I read through the code and thought, "Wow! This is devilishly clever!" As a thought exercise, I think it's awesome. But, it got me thinking about clever and production code. In my opinion, clever is never good or wanted in production code. It's great to learn and understand clever code though. It's a great mental workout to keep you sharp.
So, what's my point with all of this besides to say that clever is bad? The example in the above link is factorial (which as everyone knows is hated by me, but that's another story). But, the amazing thing about factorial examples are that they are dead simple, yet there's several ways to get the answer. Here's a few that I came up with: def reg_fact(x): Each of these compute factorial. Amazingly, there's even more ways than what I listed (the math wizards in the audience know what they are). Now, think about this: Computing factorial is dead simple. What happens when we get to harder problems? Being clever can actually get in the way of making the code easy to understand. It might even kill performance. Let's think back to the devilishly clever code. The performance of making Python tail recursive is awful. Sure, it's pure and tail recursive, but in production code that is deadly. What we want is simple and to the point. It's why I generally like solutions that need less code. There's less noise to get in the way of understanding and generally can mean better performance as well. Real world problems are hardly ever as straightforward as factorial. The balancing act comes when you drop a solution because it's not working for whatever reason. Raising and catching exceptions so you can have a tail recursive factorial is overkill. It took more code than the non-recursive version. It begs for the programmer to know their tool set and to know how to solve problems in that tool set that are straightforward. Tail recursion is powerful in languages like Haskell and Erlang. But, there's always another way of doing things that can make more sense in the language you are using. In our case, the other ways were just as easy yet more scalable for our tool set. Food for thought the next time you go down the path of clever and end up writing more noise than solution. Labels: design, functional programming, Python Sunday, July 13, 2008Functional Programming Has Warped Me
Look at this method that I recently wrote in a class for describing my database. The responsibility of this method is to commit the current session (which is encapsulated from the user) after x number of database events (adds, updates, deletes, etc). I need his functionality because I was bulk inserting objects that I had read from an XML feed. Here's what I first came up with:
The method takes a one argument block that will be called with a zero argument block that when called will increase the count and decide whether to commit and start a new transaction if it has reached the number given. At the end, I commit whatever active transaction there is. The thing I like about this is that I put the logic of counting and when to commit in one place. Here's how it would be used:
Notice how "afterAdd" is used. It is passed into the block that will handle all of the transaction adds. It is called after an item has been added. I like this code because it doesn't worry about anything, but adding things to the database. It also makes it obvious how often we will commit. But, I'm unhappy with commitEvery:during:. The reason is because it just seems like I could make it more generic and at the same time easier to test without resorting to mocks. I abstracted out the counting and put it as a method on BlockContext (this is Squeak):
This turns our first method into this:
It's a mini-DSL! Let's write a simple test. Note, this is not an SUnit test, it simply writes to the transcript. But, I wanted to show it without all of the assertions:
Gives this output:
Everything looks wonderful until we hit the last two lines. It seems pointless and unnecessary to do a begin and end with nothing in between. Our code will need to get a little bit more complex. Here's my next try:
It's more complicated. And now, I'm passing another block around to get its value. The reason was because to properly remove empty begin/end combinations, I needed to know when the before was and when the after was. This means passing a block to the afterAdd from before. So, now the test code looks like this:
I renamed afterAdd to onEachAdd which is a better name and indicates its role better. Now, the reason I wrote this post is that I sat back and went, "Wow. I have something easily testable, but might hurt some brains." My next thought was, "Why would this hurt someone's brain?" The answer is all of the indirection of the blocks. I will be quite honest at this point, I would normally start turning all of these blocks into objects and have better names for what they were doing. Blocks worked perfectly in this context and if you think about it. This code is not much different than what Collection>>streamContents: does. This is one of the things that learning functional programming has done to me. The above code looks and feels natural. Passing a block into a method that will accept another block doesn't bother me at all. I love the DSL-like nature of the implementation too, but I can see where it might confuse (Hell, inject:into: still does). This is where objects come in because now we can create our small objects with more meaningful names than just value or value:, this will at least make the indirection less painful. Thought this was fun exercise to show off something else that you can do with higher order functions. Squeak on. Labels: design, functional programming, squeak Comments
Monday, June 30, 2008Too Complex?
There's a lot of features of Ruby that I like, but there are some that just drive me nuts like blocks not taking blocks and the ampersand operator. Raganwald did a great job of explaining blocks, procs, and the ampersand in this blog post: Explanation of Ruby's Unary operator. I came away with the feeling, "Wow! It took that much explanation just to tell how to send blocks around?" If blocks were first-class citizens, Ruby would be more elegant. Raganwald would not be writing huge blog posts on block vs. proc because it would be unneeded.
Ruby has all it needs right now. Ruby 2.0 should be a pruning of features and removing special cases. Get to the things that help you concentrate on your design and not on the language. Ampersand is nothing more than noise in your code and brain. Remember elegant is simple. Comments
Sunday, June 29, 2008Weakest Link
"The deliberate use of a weak element that will fail in order to protect other elements in the system from damage." - Universal Principles of Design
Unit tests could be seen as a form of weakest link in that we want the system to fail in our tests instead of in our production environment. Pre-condition and post-condition assertions (remember Bertrand Meyer don't you?) are the perfect example of weakest links in software design. The assertion fails, then the program stops or does something intelligent about the detection: logging the anomaly or dropping an incorrect record. We have more options available to us when our weak link fails in software than other domains. I would even put exception handling under the weak link design umbrella. So, why don't we use it more? It's rare I see pre-condition assertions at all. I know the performance overlords declared them to be bad. But, when you have Rails running high traffic websites, does a few extra assertions really matter? I think not. I would rather have my program run slower with safety belts than doing something catastrophic with my data. Brian Foote once gave a talk where he equated pre and post conditions to the goalie in a game of hockey. And asked the question, "Would you want to play with or without the goalie?" It's a statement that's lived with me since. With that being said, are they places in your current design that could use a few more fuses? Labels: design CommentsSunday, May 18, 2008And we wonder why we are one of the most hated careers
This is a shame: No Dashs or Spaces. We should be helping and not being a road block. I know I've cussed at a few of these sites listed. Sometimes I think we should put ourselves in the shoes of our customer and think, "What would make their life easier?" We should make it as easy as possible for a user to achieve their goals. The computer should disappear and only the problem at hand should be on the user's mind. We still have a long way to go. Let's start being more empathetic. The users are people too.
Comments
Friday, May 09, 2008UML, Design, and Paper
Why did the UML design tools fail? By fail, I mean never gain popular acceptance. To this day, I still do my designs by hand and then do them in a drawing tool when they are solidified. The UML tool makers never understood that when designing, you need to be able to make mistakes and explore different options. Instead, they forced you to make decisions too early in the process so they could auto-generate your code. I always found it cumbersome and they got too much in the way. And that's why they failed. I love paper. All of my friends that design still use paper for their designs as well. The reason is that you can try out ideas and the design can always be thrown away. Design is an exploration journey. The tools only looked at the destination and not how to make the journey easier. The journey is what mattered.
Labels: design Comments
Tuesday, May 06, 2008I cried
I almost cried for joy when I read this post on one of my favorite topics: Small Classes, Short Methods. Someone has been reading my mind. I love the set of rules. It's something that I strive for in my code. I've run into problems with other developers though (they generally think I am nuts when I wrap things like phone numbers, ids, domain specific codes, etc into objects instead of strings). But, by wrapping the primitive into an object that states it's role, you start moving logic closer to where it belongs. The books "Genius Within" and "Prefactoring" were the books that really brought this home for me (especially "Genius Within"). Lots of great advice. Go read it now!
Labels: design, programming CommentsMonday, April 14, 2008Missing Explanations
I was looking over my last post and forgot that I didn't discuss these two methods below:
Function.prototype.everyTimeButFirst=function() {It might seem a little overkill to have the everytimeButFirst function, but I think it reads better in the method and communicates the intent. Normally, you could implement putting in the comma in between items in the collection like so:
It's a little bit longer. I hate the boolean variable "shouldAddComma". I really do. It just doesn't read well. It's hard to see what's going on. The naming makes it better. But, it's just adding unnecessary noise. But, all is not well in the first version that I used either. Not all programmers are versed in functional programming, but even a functional programmer would scoff at the first version. Why? It changes state. Plus, the implementation of everytimeButFirst while clever would not be obvious. Basically, what you're seeing is my experiment in using nothing but functions instead of a boolean. Fun for blogging and brain stretching, but not for production systems. Clever is the enemy of the maintenance programmer. But, what if we did this: Function.prototype.everyTimeButFirst=function() {You might say I just displaced the boolean to somewhere lese and you would be right. But, I got it out of the method toString which is where I didn't like it. It's safely behind a wall where it makes sense to have. The internal implementation of everyTimeButFirst was too clever the first time around. It is still using higher-order functions, but one is easier to understand than nested ones that change themselves out. Remember I said the original implementation would make a functional programmer gag because of the side-effects. We could make it side-effect free by making it recursive. But, Javascript is not optimized for tail-recursion. So, I did what I thought was best. Labels: design, javascript, programming CommentsSunday, April 13, 2008"Super" was wrong
My implementation of "super" was wrong. I did an implementation in this blog post. It was wrong for several reasons. The number one reason was because it was too complex. The first hint that it was too complex was when I was setting the "constructor" property manually. This is done for you and only needs to change if you set the constructor's "prototype" property with "{}" or "new Object()". I was guilty of the this sin. Turns out if I play by the rules, I don't need the "subclassFrom" function nor do I need to add the special "super" properties. It's actually less verbose and it's playing by the Javascript rules. When in Rome, do as the Romans.
My second mistake was that I thought each object had an immutable property "prototype". This is why I was getting "undefined" when I asked for it. I should have known that I was wrong and not the implementation. Anyway, my belief was WAY WRONG. I don't know where I got this piece of mythology from. I think I pulled it from the sky. The truth is that the "prototype" property is only on Function objects. It's set on the objects it constructs and it is not available. Mozilla and Adobe implementations have a property "__proto__" that stores it, but it is not in the standard and thus, not to be used. All objects by the standard should get their prototype from their "constructor" property. Learn something new everyday. For the new implementation, I got rid of the "subclassFrom". It's not needed now and I'm only left with the "methods" function from before. It now simply moves properties from one object to another. It's only for syntactic shorthand now. I did add something new: function "proto". It's "super" basically. Here's the implementation:
So, how do you do super by playing by the rules? Here's how: Function.prototype.methods=function(funcs) {Output from the above: base constructor called Notice the "this.proto().constructor.call" in the Sub constructor function, it expands to "this.constructor.prototype.constructor.call". This is calling the "super" constructor. We could also do "arguments.callee.prototype.constructor.call" instead, but really it's a lot to type in isn't it? We also couldn't put that into a function as easily without calling "arguments.callee". I got a better implementation and something that's more like Javascript. I like keeping my Javascript close to what a Javascript developer would expect. Prototype-based programming is powerful and it's worth exploring things without classes. But, that's a small digression. All I wanted was super and some shorthands for grouping my methods. I have that now. Labels: design, javascript CommentsAnnotations and Aspects in Javascript
Here's the moment, I've been building up to:
var logAround=function(func,name) {Here's the output: js> load("aspect.js")Not much to explain at all. I think the code speaks for itself. The output shows the arguments passed in and what the results were from the call. I didn't return anything from either method, but it wouldn't be hard to verify that it does indeed work. I implemented my own "toString" for Arrays to print out the arguments and it's worth looking at. "arguments" is not an array, but we can call array functions on it. Most of the built-in Array functions can be called on any object. You just have to use the wordy "Array.prototype.functionName.call" to use it. I wrote this for this blog post, but I'm planning on beefing it up with exception handling and un-weaving among other things. I also plan on adding more tests to my test suite that I did not show. The purpose was to show some cool tricks in Javascript and I hope it was fun. And here's the entire implementation once more: function defineBefore(beforeFunc, proceedFunc) {Labels: design, javascript CommentsAnnotations in Javascript
Last post, I talked about how to implement crude aspect advices. I say crude because the implementation would need to be beefed up a bit for wider use. The ideas were more important. I wanted to spend the article on the tricky bits of implementation. I'll take the same tactic with this article.
Let's start with what our code will look like. In fact, let's implement something everyone is familiar with: a transaction. Now, our transaction will have a blank implementation (we'll print out the important bits). It's the API that we will care about later. Here's the definition: function Transaction() {OK. There's some new functions named "run" and "define". I'll show their implementations soon. You can probably guess the implementation of "run". It's simply so that my hands don't get tired typing "Transaction.prototype". I could use "with {}", but I have found its implementation inconsistent among Javascript dialects. The next new function, "define", is how we're going to define our annotations. It's a shorthand for better readability and to reduce duplication. All it will do is add the function to our object and add the annotations as properties. Annotations are simple in Javascript because functions are just objects and we can add anything we want to them. How cool. Here's the implementations: Object.prototype.run=function(func) {Both are straightforward. But, I introduced another function: "each". Those familiar with Ruby will notice what it does right away. For the non-Rubyists, it iterates over all the properties of the object. My implementation also sends the name of the property as an extra parameter. Here's how I did "each": Object.prototype.each=function(everyFunc) {It basically allows me not to have a "for" loop. Simple. You might have noticed my use of "call". The reason is because I wanted to pass along "this" to the function passed in. That is it for implementing annotations. The query language is already built in (albeit a little verbose). We only have to iterate over the functions of an object. Here's some code I would like to use to test my annotations and query implementation:
Oh boy. Can you guess the output from the above? Here it is: begin The annotations I have is to mark a function to be logged on entry and exit. I'll spend the rest of the article on the implementation and bring together the annotations, aspects, and logging in the next article. Let's talk about the new functions: "onlyWhen" and "eachFunction". I think you can guess what each of them do. "onlyWhen" is defined on a function and takes a function to check the arguments for some condition. If the condition is true, it executes the receiver function. It returns a function that brings all of this together. This is more functional programming at work. "eachFunction" takes a function that will be called for each function that is a value of a property on the object. Like always, here's how I implemented them: Function.prototype.onlyWhen=function(ruleFunc) {In the "onlyWhen" implementation, I have to bind "this" (the function) to self so that we can use it later in the function that we use a result. "this" will be bound to a different object. "eachFunction" brings together the functions "each" and "onlyWhen". That's it! You could of course add implementations to define annotations so that we make sure that only pre-defined ones are used in functions. I'll leave that as an exercise to the reader. It wouldn't take much to implement it. The next article will put everything together. All we have to do now is weave based on rules. We have the means to iterate over functions and we have the means to replace implementations through our advices. The next article will mix this batter. Labels: design, javascript CommentsFriday, April 11, 2008Aspects in Javascript
Just like I promised! I'm going to describe how to do aspects in Javascript. Now, this example will be simplified. You will probably want to beef up the implementation given here. I'm really showing off an idea and how you can apply it to your Javascript libraries. I did this for fun and was shocked how easy it was. In these examples, I will be using Spidermonkey.
I'm first going to show off how to implement advices. Let's start with "before" advice and then have a simple test for our implementation. I would recommend using JSUnit for testing. I'm simply going to output it for demonstration purposes. The easiest advice to implement is "before". All we need to do is define a function that creates a new function that calls our before function and then calls the function we originally wanted to call. "defineBefore" handles this quite nicely. The rest of the code below is defining our "before" advice and the function we want to call and then doing the wrapping. Pretty simple.
Here's the output:
You can see from the results that our "before" advice implementation can look at the arguments (but, not change them, we'll get to that). And be basically benign to the whole operation. Our weaving works! "After" advice is equally as easy. We just need to remember the result and return that from our weaved function. Again, the "after" function will be benign and not be able to change the result. Here's the implementation:
Here's the results:
Now, our "before" and "after" advice weaving did not allow us to change the arguments or results. I wanted to keep it simple without a lot of complications. If we need to change the arguments or tamper with the result, let's do it with an "around" advice. The implementation for "around" is a little bit more complicated. I want to make calling the proceed function as simple as possible in the after advice function. To do this, I need to wrap the proceed function with another function that will bind the "this" so that we don't have to worry with sending "call" on the proceed function. The implementations before didn't have to deal with the proceed function in the advice and that made life easier. Now, we'll have that control and with that power, comes the ability to change arguments and results. Here's the implementation:
And where would we be without the results?
One more detail, I didn't do an error handling or recovery. You will probably want to wrap any kind of exceptions that might happen in the proceed function and take appropriate action. So far, our implementation is nice and clean. We're mainly using functional programming concepts for it. The next steps is writing the implementation for the pointcuts and combining that with what we did here. I'll also going to show how to define annotations. Trust me it's going to get more fun as we combine the batter and get ready to bake! Labels: design, javascript CommentsTuesday, April 08, 2008Javascript Inheritance with super
One thing that has always bothered me about Javascript is there is no implementation of "super". Most texts suggest to simply call the super function explicitly via its prototype. It makes for refactoring your inheritance hierarchy no fun because of the direct calls. And let's not mention all the duplication.
Let's think for a minute. Everything in Javascript is an object. What if we added the super automatically to the overriden functions so we could get access to it? And while we are at it, why not make our object definitions a bit more explicit along the way? So, I whipped up the following code:
It's two very short functions. But, it allows me to do this:
"arguments.callee.superFunction.call" is a little verbose, but it's better than duplicating the name of my "super" implementation prototype all over the place. Plus, how often do you call "super". I generally think too many calls to "super" is a design smell. I like this approach mostly. The only thing I don't like is the names I chose. But, I'll keep working on it. I was just happy in two functions, I have "super" in my constructors and functions. If anything, it's a different way of thinking about the problem. Coming next, how to do aspects and annotations in Javascript. Labels: design, javascript Comments
Friday, April 04, 2008Monkey Patching
Monkey patching seems to be all the debate rage currently in the dynamic language blogosphere. The one that caught my attention was Gilad Bracha's in particular. I don't disagree with him at all. I know it's strange reading that from a Smalltalker. But, I view monkey patching like inheritance. It's a great device to have in your programmer back pocket, but can be dangerous. Use with caution and look at your options. Don't use it without thinking of the implications it can cause. The reason its dangerous is because it's a global change that widens the protocol of the affected object or worse changes the contract for an already existing method.
The main reason monkey patching is so dangerous is the global change it makes and the increased probability of having a collision if another project names a method the same. Or worse, another project overrides the default behavior and replaces it with their own. Both can cause subtle bugs that can be hard to catch and find. It's the global nature of it that can bite. It's convenient, but you should think twice before doing it. It's limits your project's options. Groovy has a novel approach to monkey patching in that it allows them to be scoped for the duration of a block called categories. This reduces the risk of a global change and the addition is only in affect for the duration of the block. Now, it can cause issues if something gets called outside of the block by accident, which is why I wish it had different ways of scoping (class, package, etc). But, with AspectJ these problems can be reduced. Categories reduce a lot of the risk of monkey patching in that collisions are highly unlikely and the protocol is only widened for that invocation. It also ensures that unknown project dependencies will not creep in. I worry less about monkey patching in this instance because if you get burned, it will be your project not everyone else. I think categories are the way to go (if they added class scoped categories, I would be in heaven). I don't hate monkey patching. Far from it. It is handy to override method implementations during debugging. I can change pre-existing methods if they have bugs in them even before the next release is out. I don't have to wait on the vendor. These are great pluses. I'm just advocating thought before you monkey patch in your own projects. Much like before you swing the inheritance hammer, you should do the same with monkey patches. Labels: design, programming, ruby, smalltalk CommentsWednesday, February 13, 2008Yet More On Tools
I got so many good comments on Tools that I couldn't just let the replies go to waste. Here's another one from Sam Tesla:
Sooner or later I was going to bite on one of your posts. My answer to this is simply why write a new language to begin with? The reason is simple: to solve a specific problem better than before. We shouldn't stop trying. Now, with that being said, let's move on. I know Erlang is a powerful language and have many times sang its praises. There's a lot of languages in the same boat (Io, Mozart, and many more). I can understand not wanting to do another editor in the beginning, but it's a good exercise. There are non-trivial problems to solve in doing your own editor. Plus, it allows new developers to see a non-trivial example of your new language's code. Again, let's move on. My main rant was on tools (not necessarily editors), yes, I know I picked on Eclipse and Emacs. But, Eclipse is really much more than a text editor. It has several tools to reason about your code (Refactoring), give your different viewpoints (Call Hierarchy, Class Hierarchy), and ways to ignore what you don't care about. These are the kinds of tools that I am interested in. I think a code beautifier and debugger are the price of admission. You are not even playing the game without those. Now, Erlang is a different way of thinking, it should have tools to support that thinking. Much like the Refactoring Browser changed the way we view code in Smalltalk. I think Ruby is ripe with opportunities to change the way we think about code. Again, the tools should reflect that. I'll use a simple example: AspectJ. Now, it's not a language per se. But, it's a different way of viewing code and a different way of developing. I liked the idea of aspects, but it didn't click until the Eclipse support came. Once it was easy to see the consequences of my actions, my mental model became stronger. It also made it easier to reason about what my systems were doing. Tools should aid and enhance your mental model. They should allow you to ignore details and quickly hone on the ones you do. Lastly, I'll add that I wouldn't even think about doing Java code without Eclipse. It's the tools. But, when I'm programming in another language like Ruby for example. I want to stay thinking in that language. So, if I have an idea for a nice tool, it should be easy to whip it up in the language I'm working in. I shouldn't have to switch gears to another language. We should think of languages as playgrounds to change the way we program and change it for the better. Labels: design, programming CommentsMore On Tools
Friedrich had this to say about my Tools post:
First off, I could give the quick flippant Smalltalker response and say, "If you need a text editor that can handle large chunks of text, then you're doing something wrong." But, I'm not. Sure, we don't need to handle large chunks in coding for Smalltalk, but the world is a different place now. The need is there. Today we need to handle XML documents, source from other languages, HTML, and that's just a start. But, an editor that can handle large amounts of code and makes it easier for new Smalltalkers to get them into the swing of things so to speak. Secondly, the Smalltalk community might not know they need it. Write it! If anything you might deficiencies through the exercise and decide to fix those as well. I wrote a Java Serialization framework to learn its format, but to fix bad streams as well. Nobody wanted it, but me. You got to do things for yourself and for your own journey as a developer. If you need any help, feel free to contact me. But, don't expect any community to follow along. The best you can hope for is to make the music and hope other people like the melody to join in. Labels: design, programming Comments
Monday, February 11, 2008Tools
I'm a firm believer in that tools should be written in the language that they are to be used for. I always treat a language with caution if the only tools for it are written in Emacs Lisp or Java and not itself.
But, why does it matter? It matters because writing tools for your language in another language tells me something. It tells me that:
Any of the above points are bad in my book. But, I see it constantly in new programming languages (even in ones 10+ years old). Now, I love languages and learning them. Why? Because I always walk away with a new way of thinking about things. It's also nice to see how other people solve certain problems. It shocks me though that so many languages depend on Eclipse or Emacs for their tools. At some point, a language moves from command line pet project to a true programmer amplifier. The language has to be elegant in syntax, yet have the tools to make debugging and reasoning about the system simple. Now, before you get angry with me, I do realize that using Emacs or Eclipse gives a huge boost in the beginning with their frameworks. But, by never leaving them, it could also mean there are holes in your frameworks or in your language itself. Tools written in your language help to expose issues like limited reflection or that your language is hard to parse. I believe writing tools should be easy and for everyone to do. If writing tools in your language is hard, then you might need to redesign your language. I think if we had more languages that had their tools written in them, we would all benefit. Plus, with tools written in your language, new developers have some example code that has real world applications. The way to prove to me if your language is elegant is not to show me how easy factorial is to write, but to show me how easy it is to write an inspector. Labels: design, programming Comments
Wednesday, November 28, 2007Some Metrics
I ran the metrics, that I talked about in my last post, in three environments and the results are above. Nothing really shocking, but the maxes did surprise me. I was mainly interested in the averages and the maxes were there just to see how huge the big boys really are. I wonder what developers would think of a language that restricted the number of things that they could define. What if by looking at the numbers of above, I designed a language that didn't allow more than 256 methods/class, 8 parameters/method, and 16 fields/class. It would force the developer to write smaller entities, but would it annoy more than help? Where I'm going with this is simply, what if the language enforced certain hard restrictions instead of allowing any incredibly large number of possibilties? If elements were kept to a certain size, would it make programming in the system more pleasurable since it would be difficult to create a god object? Objects would not get out of hand before refactoring thus making maintenance easier. My gut instinct is that constraints would annoy and ugly code would still live. Developers would just figure out ways around them. I would love to think that by adding constraints, we could get better code. But, no matter what you measure quality by, there will be developers that will figure out how to get around it. Pessimistic, I know. But, it's simply human nature. Still, I wouldn't mind if the language did have some limits, so that individual items stayed small. If the constraints were not dogmatic, it would make the need to get around them less attractive. Of course, with smaller things comes naming them and that would still be a problem. But, that's a problem with mammoth sized objects as well. I'll keep thinking on this. There's got to be a way to have flexibility to dream the impossible and yet gently nudge us into more maintainable programs at the same time. The problem with balls of glue is that slowly become that way. It would be nice to not allow them to become Godzilla. Labels: design, programming Comments
Tuesday, November 27, 2007Limitations Of The JVM
What's the point of the list above? Sure, those numbers are reduced by various factors (like this reduces the number of method parameters by 1). But, that is not the point, my friends. The point is all of the above numbers are HUGE! I was reading my trusty copy of "The Java Virtual Machine Specification Second Edition" when I got to Section 4.10 and read some of the above bullet points. In Java, I live in the land of the plenty. I know it's all two byte boundaries and all, but what if they left some of these at one byte or 4 bits? I can hear it now: "But, we have all the space in the world! Why constrain ourselves in such a way!" True. But, when was the last time you looked at a method with 80 parameters and thought: "DAMN! Now, that's some beautiful code." Exactly. You never have and you never will. The point is not to constrain on a byte boundary, but on a good coding boundary. Arbitrary numbers are awful to use as metrics (like you must have less than 3 arguments to each method and so on). Metrics are useful in relation to something else. You pick what seems reasonable to you (which might not be reasonable for someone else). There is a boundary where any reasonable programmer starts to hurl at bad code though. Metrics are set below that for each team. I hope I never see the interface with 16,000 superinterfaces. I really do. The cool thing is we have a huge library in Java to look at to see what is reasonable. We have tons of open source projects to feed our reason as well. What if...What if we picked what was reasonable from these code bases by using averages and everything else in our numerical power? Then, we doubled it to make it unreasonable. I wonder what the numbers would look like? My next experiment is to run some code reflecting over both Java, Ruby, and Smalltalk code. I wonder what the averages for each would be (number of method parameters, number of methods per class, number of fields, and number of local variables). I'm curious. I have a guess at what the numbers will be. But, the raw numbers could be telling. I would like to see them for all three of the above languages. It should be interesting. I know there's probably someone out there that has done the same thing, but I want to run the tests on my own. For now, I will do it with the base libraries of all three. My guess is that they will be roughly around the same for each of the metrics that are comparable in each of the languages. What's the point? By reading and then thinking of the worst, it got me thinking. Our languages constrain us in ways that they shouldn't, but don't in what would make our code better. Would it be such a bad thing having to split a method up because you ran over a rather large (let's say 256) number of local variables? I want to find the number where it seems unreasonable and grotesque and make that my limit. I know 65,535 is absurd for the number of local variables, but so is 256. What about 16? My answer is "HELL YES IT IS!", but there are those that would argue that sometime you need that many. 16 is not hideous, but is still in poor taste. I'm off to see the wizard. I'll let you know what he says. Comments
Thursday, November 08, 2007Bug Finder
Two of my favorite tools to use in coding are: a code checker and a code beautifier. The first points to sloppy and rushed code. I find sloppy code to be a vital protein for bugs to grow. The second shows me a consistent view of the code. It's amazing how many bugs you can pick out by just running a code beautifier and glancing over the code. I'm always amazed what I find with a code beautifier. What's even more shocking to me is that a lot of developers hate them. Even if the formatting is less than to my liking, I still use it. Why? It catches so many moments of unclarity. It also points to code that should be broken up into smaller bits. If the code beautifier formats my code to be ugly, then I need to refactor. It's a rule of thumb that has served me well.
Of course, I usually run the code beautifier to get a lay of the land on new code and roll back when I'm done. Why? There's nothing more annoying than searching through tons of code changes to find out that the code was only reformatted. Now, if it's my code, I don't check it in until a code beautifier has been ran on it. I find studying my code after running it is just a good habit to get into before check-in. Reading code that's normalized makes it easier to spot patterns of inelegance. I'll write more about the code checker, also known as Lint in some circles, in another post. Labels: design, programming CommentsTuesday, October 23, 2007Bloat
John C. Dvorak asks the following in his latest blog entry:
But I wonder if all open-source projects will eventually go the way of all software. Upgrade after upgrade adds more and more complexity, to the point where the software begins to get bogged down under its own weight. How many open-source projects have you seen in which the code gets leaner and meaner rather than fatter and fatter? Good question. Software becoming more complicated and bigger is almost a universal truth. I don't see why open-source should be immune. Of course, the more features you add, the bigger your program will become. But, even software that is being maintained becomes an entangled network of goo. Brian Foote even wrote an anti-pattern about it called "Big Ball of Mud". This is the problem that needs to be solved. I see a huge focus on creating tools that allow quick creation of code. But, I see few dealing with complexity of existing code and even dismissal of the ones that do exist. Refactoring tools are mandatory in the fight against creeping code. Tools that allow diagnosis of live running systems is a must. Code generation gets my code out the gate quicker, but without a gym, it becomes slow as molasses as the race continues down the path of new features. I want to create systems that in their evolutionary cycle grow consistently with new features. The complexity should be in line with features. Right now, this becomes exponential and is proven by ever rising maintainance costs. Our current weapons are refactoring tools, code checkers (lint), metrics, debuggers, and let's not forget the most important: education. Actually, could that be the answer? Discipline and education just don't have the same kind of sexy ring to them, but tools can only take you so far. Still anything that can help our brains reel in the complexity is a welcome addition. Software is only going to get more complex as we go along. I think we spend so little time on reducing complexity in existing code because the feedback is not immediate. I want to get to the point where I can deliver the third version as fast I delivered the first. It's a shame with all of the advances in our field that we have been unable to keep maintainance costs down. There are developers working on this problem. I've heard rumblings that tools are in the works. One possibly being a more macro level refactorer. I can't wait to use it. Until then, I'll keep my lint, refactorer, inspector, debugger, and metric runner close by my side. If anyone has any other tools that they like to use to keep complexity down, I would love to hear it. My brain can always use help. I'll end with this all too true quote on Firefox in the same article as mentioned above: Just look at Firefox, one of the poster children for open source. It loads more slowly than ever. It will be on some page within a tab, and that page will be refreshing ads or something in the background, and Firefox just hangs all its cycles there, slowing down the whole computer. Every couple of days the browser is hogging cycles, and I have to go to the Task Manager and kill it. Other people have this problem, too. Go read the rest of the article. He makes some good points on mindless fans of technologies. Sadly, I think open-source is going to be the place where the tools to deal with complexity will be born. Until then, open-source will suffer the same as the rest of us with bloat. Labels: design, programming Comments
Monday, October 22, 2007"Considered Harmful" Considered Harmful
It's official. "Considered harmful" can be added to list of cliches to avoid. It needs to go into retirement along with factorial and prime examples.
Labels: design, programming, rant Comments
Monday, September 24, 2007Good Design
I love Alex Ruiz's post on named parameters in Java. Why? For one, it shows how he got around Java's lack of named parameters. He then goes on to explain why he made the design choices he made. Pros and cons are then listed for the approach. But, the part that I loved is that he sided on the side of the API user. It made his life as the API designer harder and cost him more code to write. The result was something that allowed for more readable code for the client. I love it! I tend to design with making the client's life easier instead of mine. This article is excellent for outlining the design choices and not being dogmatic. Everything is logical and well laid out. Another reason I loved the article is that the lack of a language feature inspired him to work harder to get the same result. A post that inspires us to push for better solutions no matter the constraints.
Comments
Sunday, September 16, 2007A Sorting Language
Inspired by Neal Ford's post on patterns and sorting in Ruby:
I loved Neal's post, but it got me thinking that sometimes I need to sort beyond one field. How could I go about that? I whipped up this example really quick. The function :by is simply syntactic sugar for converting to a comparator. I thought it read better. Neal's solution is the way to go if you need to sort on a single field. But when you need more, the above will work too. This is another one of my little late night coding thoughts. Enjoy. Comments
Friday, September 14, 2007Python's Reduce, (Fun)ctional Programming, and Ruby
Mike Hostetler blogged about Python's reduce. He said he never had a use for it until recently. I looked at his example and I thought that looks a lot like inject, but it uses the first element in the collection as the seed instead of it being given. I thought what mad fun would it be to write one in Ruby? Exactly. Here's my implementation:
It has a somewhat functional style in that instead of using a boolean to check for the first iteration, I just use a special lambda for the first pass and then it turns itself into the original one. There is some state (to_execute), but I'm still a functional newbie. I do love inject, which has a lot of uses beyond aggregation, and this will reduce (no pun intended) code in some places where I use it. Anyway, I couldn't stop myself. I thought wouldn't it be nice to just pass in a symbol instead of a block? I added the following method to Symbol and another test. Check it out:
All I do is take a list of arguments. I make the first one the receiver and send the rest as it arguments. How cool is that? I get to cut down on the line noise. I still have to put up with &, but until they make blocks first class citizens... What's the point of this post? Nothing. Just wanted to do a fun little exercise. I thought others might find the implementation fun as well. Now, about that name 'reduce'...I think maybe aggregate would be better? But, that doesn't express for all cases either. Hmmm... Comments
Tuesday, August 28, 2007String Concatenation
I made a small mistake in the code in "Promises and String Concatenation". The DelayString object not being stateless is a huge problem. Here's the test to prove it:
def test_independence Ouch. I fixed it by making DelayString only know a left and a right part. It cleaned the code up quite a bit. I factored out Promise because it made the code a little less readable. The resulting code is much simpler: class DelayString All of our tests run. DelayString is stateless (minus caching of the result). There's still improvements to be made, but the code is simpler and easier to understand. The performance did take a hit. It's twice as slow (43.94s) as the previous version. Not to worry it still beats normal concatenation by a large margin. I'll take the performance hit for more readable code anyday! Labels: design, programming, ruby CommentsSaturday, August 25, 2007Promises And String Concatenation
You've read about it a million times. Beware of multiple string concatenations. They make your code slow and consumes memory. You've read it in the Java books (known as "use StringBuilder or StringBuffer instead of +"). But, why? Why isn't this handled at a lower level? Why can't the VM or compiler just do the right thing?
Messages are the power of objects. So, why not make a new object that when sent the + message, it simply returns an object that waits to do the concatenation until it is needed. This new kind of object should understand the same protocol as string. This could all be handled underneath the covers. If it was done at the VM or compiler level, programmers would never have to know. I thought I would do a sample implementation. It's rather easy (in a dynamic language). First, we need to implement a Promise class and here's the Ruby code complete with a simple test: require 'rubyunit' Pretty simple, huh? Create a new Promise object on a block (or closure or lambda or whatever you like to call it) and it will only call the block once when the message "value" is sent to it. If the message "value" is never sent, the block is never evaluated. Can you think where that might come in handy? I can think of several, but the best one is when trying to create a message to log. If you don't log the message, you wouldn't need to do the concatenation. Again, not doing the computation upfront can not only allow us to manage memory better, but also not to do needless calculations. Enough talk, let's get to the good stuff, right? Here's my implementation of delaying concatenations and check the tests out at the bottom: require 'stringio' One new class called DelayString handles not doing the concatenation until absolutely necessary. It does this by creating a Promise that calculates the string by using a StringIO object (Stream or StringBuilder in Java terms). All it does is keeps a collection of all the strings it needs to append to one another. The power is now that we get the nice succinct message "+" and all of the benefits of using a stream object (or StringBuilder). Of course, we would need to add more methods on our DelayString so that it has the same protocol as String. A little more work to make our implementation seamless. Below is the test method I added to find the times it took to run for delayed and normal concatenation: def test_performance The new delayed implementation ran at 23.8 seconds. Not bad to do a million additions and a lot of little ones at that. Now, what were the results the old way? Well, all you have to do is comment out the + message: # def +(another) It took 4565.68 seconds to run the normal way. It performed poorly and took up a bunch of memory. Yuck. It's what the books warned us about right? It's what we expected somewhat. I didn't expect how much of a performance gain I really got. Pretty cool, huh? Amazing. It's unlikely that we'll do something to this extreme in the real world. But, wouldn't it be nice to not worry about performance in our regular code? If we find that our implementation is sub-par, one of the new benefits is that we can change it in one place. Wait a minute. We just got better performance and got to keep the simple way of doing things? Not one lick of our already existing code had to change. The power of messages is powerful indeed! Labels: design, programming, ruby Comments
Monday, July 16, 2007Bad Code
I have a thing for bad code. In fact, I've been talking every month at the Omaha Java User's Group about it every month by refactoring ugly code. Well, I ran into this article about poor practice in coding examples. Don't worry give it a quick click. It's bewildering no matter how much technology changes, bad code remains. What's even more worrisome is I see the same errors in code today. It makes me wish they gave out Code Complete to everyone.
Labels: design, programming CommentsTuesday, July 03, 2007More on Exception Handling
Another great comment from Malcolm Sparks:
I complete agree with you Blaine about not throwing away information. Keep everything, and add to that information whenever you have something to add. For example, when a caller catches an exception, the caller always has some indisputable and unique information it can add: exactly what it was trying to attempt when the failure happened. What a great idea! I love it. I agree if you are checking for generic exceptions and then wrapping them into more domain specific for more information, then rock on. My original comment was aimed at catching generic exceptions in your code and then doing nothing with them. I still generally try to catch the things that I expect to go wrong and use my top level to catch things I didn't. Always adding more information is a good idea in my opinion especially if it gives you more context. As with any rules, it really requires thought and good judgement to make a good program right? Thanks for the suggestion! Labels: design, java, programming Comments
T.H.O. Part ][
Malcolm Sparks had this to say:
I think that we a few more people with that T.H.O. attitude to prevent open-source libraries from stagnating. It is a good thing that the incumbent 'standard' library gets regularly challenged by contenders. Without continual selection and reselection, evolution dies and progress stops. I think you might have misunderstood me or I didn't make myself clear. My point is if you have want to do something better, then do it in the open source community. Go at it and have all the fun you want. I think the open source community needs and thrives on forward thinkers. At least there, you will have feedback, tons of people testing, and a chance for your idea to mature. But, I believe doing that on a corporate project is risky and prone to maintenance nightmares. Why? Because generally they have not gone under the same scrutiny an open source project has. When I learn some new technique or have some crazy idea, I write it in my spare time. If I like it, I'll publish it on my site. Now, there's a lot of things that I did (like LazyCollections) that I would never do on a real project. Why? Well, LazyCollections was a chance for me to play around and try things out. I thought the experiment might be a helpful learning exercise to other people. But, the code is generally hard to understand for most developers. I have empathy for the people that will have to maintain my code. Generally, this means I don't code things that I can, but what can be easily tested and maintained. It's not dumbing down the code or the design. Simple designs don't need tricks. They just need thought and lots of it. Simple designs are not easy and generally not the first solutions you come to. They are hard work. Labels: design, programming Comments
Monday, July 02, 2007Good code tells you where it hurts
I've been meaning to write an entry on exception handling and how important it is to not throw away information. So, here it is. I'll start with one my pet peeves below:
try {OK, first off, I'm usually skeptical of catch all blocks at low levels. But, there can be good reasons for them, but the problem I have with code above is printing the stack to the standard out and continuing. At the very least, this is only good for when you are doing command line utilities and even then it's not good. Worse yet, this is the default from Eclipse! I've been bitten too many times where code went wrong and we couldn't find out why because the stack was written to the standard out and we couldn't see what it was. Or maddeningly the code continued and caused other problems further down. Fun to debug those problems it really is. My solution is if it's informational and you can continue log it, if not re-throw the exception. But, there's times when you catch a checked exception, but it's something that's serious enough to stop current processing. I've seen this: try {Thankfully, I haven't seen the above code so much since 1.4 added support for wrapped exceptions. But, I still see it. The problem with the above is the original exception is dropped. When debugging or trying to find a problem, the above code gives me no information of the original context. It's useless, but it did stop bad things from happening further down. But, it would be almost possible to find out what really went wrong. Always catch the most specific exception and leave the catch all exception handlers to the top level code. It will save you headaches, I promise you. My rule of thumb to exception handling is: if something goes wrong, what would I want to know? I see too much code where this is forgotten or it is written as if nothing will go wrong. In a perfect world, all code works beautifully and there is no need for exception handling, but we don't live in that world. As always think about helping the poor developer that has to come behind you. Labels: design, java, programming Comments
T.H.O.
T.H.O. used to be an acronym that a friend of mine would use to describe code in which it was apparent that a developer put his ego before the good of the team. The code exhibits clever behavior when a simple approach would have sufficed. But, the developer had to show off that they knew a certain trick. Sometimes, it can be more sinister. A complete library written from scratch when there are open source equivalents better tested and designed. But, they just couldn't resist doing it themselves (also known as the "not invented here" syndrome). They think they can do better. But, normally, it is some poor maintainer that has to deal with their "better" solution after they are long gone to the next T.H.O. In this day and age, I can't see how anyone can justify writing a library from scratch if there is an open source equivalent. It boggles my mind. Of course, if you think you can do better, then do so on your own open source project and live with the code before causing a maintenance nightmare for years to come. Besides, you'll get valuable feedback and fix bugs from other developers. Value simplicity and doing the least to get the most.
Labels: design, programming, rant Comments
Friday, May 11, 2007Idea Killers
Recently, I've loathed hearing the phrases: "YAGNI" (You Ain't Gonna Need It) and "Is that the simplest possible thing?". My gut becomes knotted and disgusted. It makes me not want to be considered agile anymore. Why you ask? I always saw agile as positive forces in the software industry. I welcomed them with open arms because they were a shot of fresh air and made everyone think about how they did things. But, now the mantras are being used to be idea killers.
The mantras were meant to shake your thinking. How could you do this as simple as possible and still work. A nod to good engineering and design to shake the brain to think of another solution. Or ask questions like "Do you really need that huge framework?" But, lately, these mantras have been having the opposite effect. I've been in meetings where they were used to shoot down good ideas. And they work well. It's hard to argue against. You don't want to be the person that doesn't want the simplest solution right? But, when you are at the phase of generating ideas, these critical phrases kill the creative spirit. I say no more. I say no more mantras. Let's think. Let's generate ten great ideas and then be judgmental of each one. At the idea phase, all ideas cost the same. Crying "It's not the simplest thing" too soon kills good design thoughts dead before they are allowed to grow. I think these phrases have become too common place. They are used to shut off thought and prove you are right. Besides, it's been a rare event when someone meant truly simple and not easiest. The two are confused frequently. Simplicity takes practice and thought. There's a reason why Einstein said it. It took several rewrites to get Smalltalk to its current state of simplicity. Think about it. Refuse the use of mantras. And let ideas flourish. Labels: design Comments
Wednesday, May 09, 2007Live Refactoring May 15
I got talked into doing a "live" code refactoring at this month's Omaha Java User's Group with Sam Tesla. It will happen on May 15. Actually, you need to come because we don't know what code we will refactor yet except that it will be horrendous. You will see the ugly come elegant and simple! Sam and I did a similiar "live" refactoring for the local Software Process Improvement Group not too long ago. It went well and was a lot of fun. It's fun collaborating with Sam and our styles compliment each other very well. Of course, the code will be in Java and we'll be showing off what you can do with Eclipse. It's rare you get to see the journey to elegance and this will be an opportunity not to be missed! I hope to see everyone there.
Labels: design, java, presentation, programming Comments
Sunday, May 06, 2007DSLs: What's the big deal?
I've always wondered what the big deal with DSLs was. Now, i'm not saying they are bad quite the contrary.But, I believe a DSL is a healthy bi-product of a good object-oriented design. So, I was a little annoyed with all of the talk and the tricks. I kept thingking to myself, "But, if you did a good design, you would have this!"
But, I should really be kicking myself. I should be glad that good design is back in vogue. and you know what? I am. I'm not annoyed any long and I relish all of this new talk on DSLs. I even feel guilty. You see, DSLs have always been part of the Smalltalk farbic. It's natural to us because it's the way we have learned to code. I remember having the mantra "Code should read like a conversation" shoved down my throat until it became second nature. The cool thing is that there is a whole new generation finding out about this and doing it. Very cool. I'm now promising myself to show non-Smalltalkers the other cool things that we have in our fabric and what we take to heart. The power of messages compels thee! Labels: design Comments
Sunday, April 29, 2007Built-in Persistence/Transactions
We have long now been able to let the computer handle memory management in our programs via garbage collection. Why not let it also handle persistence and change management? I think these are things that should be built-in and it would be handled more easily by the virtual machine. There could be a low level API for hooking up different persistence strategies (relational, object, etc) and code would no longer have to be dirtied up with SQL or even worse HSQL. At the very least, change management of objects should be handled by the VM. How much easier would exception handling be if you could also rollback all the changes that had occurred and you could get back to the state you were in before the error? I think these should be the new primitives.
Gemstone is the realization of this on the server. It would be nice to make the code on the client as transparent as well. I know some might laugh at this idea, but I think it has merit. Just like garbage collection seemed strange at first, why not allow automatic persistent management as well? Labels: design, inspiration, programming Comments
Re: Make Debugging Tests Easy
Boris Popov left a comment on my post "Make Debugging Tests Easy":
Well, I would probably change the file to a stream, but that's a different topic and discussion (I generally don't like to access outside resources in my test; they only cause problems later). Back to the conversation, the last two lines are what I don't like. The reason is that say another developer changes any of the code that gets called during the loadPaymentFile: and they run all tests. And let's say it's in common code and this test fails. Let's also say that we get back 2 valid payments and 2 invalid payments. The developer is going to have no clue which one became invalid and what made it valid in the first place. You could put in comments explaining what the 3 valid payments and 1 invalid payment were to help. or you could break out and have several asserts for each of the cases. Otherwise, I have to revert code and rerun this test to see which payment is now incorrect. And then, put my code in and see where my code is incorrect. The above code makes the original intent hard to understand. Why were only 3 valid? Why was only 1 invalid and what made it invalid? Really the above test is not incorrect per se, it's just not very thoughtful of the other developer's time. A couple of extra minutes of coding time would save the other developer a couple of hours. It's about coding and feeling sorry for the poor person that has to come behind you and figure out what you did. I try to always code with the maintainer in mind. Most times, the maintainer happens to be me and I'm always thankful I took the extra time to help myself. CommentsThursday, April 12, 2007Tiny Types, Abstract Data Types, And Little Objects
Darren Hobbs wrote about one of my favorite topics: Tiny Types. I call them abstract data type, but I like Darren's name better. The idea is the same. I think it's just good design. I once thought that tiny objects were too much as well. But, I now think tiny objects is the only way to go.
Tiny objects make unit testing easier, aid in reuse, stop duplication dead, provide for better messages when things do break, puts functionality closer to where it is used, and I could go on all night. The amazing thing is at first I started to use tiny objects to put constraint checking in so that it didn't get propagated everywhere. But, something strange happened. These tiny objects started to take on more functionality and have real protocols beyond just get and set of their values. I think tiny objects are even more important in dynamic languages than in Java. They can make stupid programming errors easy to find and correct. Also, your functionality is spread across several single responsibility objects working in concert to provide complexity. Tiny objects are good design period. Labels: design, programming CommentsTuesday, February 27, 2007Where Did Use Cases Go?
I've always liked use cases as an analysis/design tool. I was even the lead developer on a now defunct use case management tool. What ever happened to them? I never hear any one talking about them anymore. Sad really. They were an important part of the journey to understanding user needs. It made gathering easier as well as grouping functionality together. When done right, domain modeling was a breeze. Sure, you never got all of the use cases before design started, but at least you had a good idea of what the goals of the system are. I'm still shocked when I get on a team and the goals of what the software is to do is not clear.
It seems in the rush for agility that people threw away use cases as well. I don't know why. I think stories are a horrible mechanism for understanding. They are the lazy man's use case. Modeling the main domain objects and a good cut at use cases is crucial before you start coding. It will save countless hours. Now, this might seem un-agile. But, thinking about your problem and trying to get a good grip on it before you start coding IS agile. Writing use cases does not end when you start designing or when you start coding. It's a continual process. Use cases are needed to be not only for the knowledge of the developer, but also so that the client knows exactly what you are building. They are to be done together. And as any experienced developer will tell you, you never know everything about what you're building. Customers and developers need each other. Use cases are proof of that. Use cases rock. CommentsSunday, February 04, 2007Books on Design
In one of my comments, someone asked me the book that I would recommend from Rebecca Wirfs-Brock. And I thought why not just put some of my favorites into a list? So, here's my list in no particular order, but all of these come highly recommended:
Labels: design, inspiration, programming Comments
Sunday, January 28, 2007Some Self Philosophy
From the Self 4.1 Programmer's Reference, Chapter 4, A Guide to Programming Style:
In short, to maximize the opportunities for code reuse, the programmer should: This is the summary to a portion of the chapter entitled, "Behaviorism versus Reflection". It's best explained in this three paragraphs: One of the central principles of SELF is that an object is completely defined by its behavior: that is, how it responds to messages. This idea, which is sometimes called behaviorism, allows one object to be substituted for another without ill effect—provided, of course, that the new object’s behavior is similar enough to the old object’s behavior. For example, a program that plots points in a plane should not care whether the points being plotted are represented internally in cartesian or polar coordinates as long as their external behavior is the same. Another example arises in program animation. One way to animate a sorting algorithm is to replace the collection being sorted with an object that behaves like the original collection but, as a side effect, updates a picture of itself on the screen each time two elements are swapped. behaviorism makes it easier to extend and reuse programs, perhaps even in ways that were not anticipated by the program’s author. Basically, the above is basically placing more value on "duck typing" (the new word for behaviorism) than reflection. I've always placed myself in the "behaviorist" camp and anyone that knows me rolls their eyes when I rip into my "@#$%^& not another data structures and controllers architecture!" It's because I place more value on the behavior than I do the data. It has a lot to do with my mentors enlightening me to the teachings of the brilliant Rebecca Wirfs-Brock (yes, she is still my hero). Sorry for my digression, but why did I quote all of this? First off, I always thought of duck typing and reflection as tools in my bag. I have never thought about why I would pick one over the other. I do tend to pick non-reflective solutions where I can (It seems Joshua Bloch does the same from his Effective Java book as well). The reason being that most people understand behavior, but reflection is not so obvious. OK, enough background, and on to my real point. This all got me thinking about why I've always been squeamish with reflective GUI, persistence, and rule engine frameworks. Now, before I begin, I love my OO-relational mapping tools and rules engines, but somehow they have always seemed like they were breaking encapsulation. And in fact, they are for great benefit (which outweighs the encapsulation violations). They are going underneath the covers of your objects and exposing them to a privileged few. Now, this gives us great power and takes a lot of things out of our hands. These frameworks do a lot of heavy lifting and breaking encapsulation has always seemed like a small price to pay. But, how could we do it without reflection? Ah, there's an interesting question, no? Is this mental aerobics going to get us anywhere? I don't know. But, I bet the journey will be fun. So, what do we have in our arsenal right now? Well, off hand you can name the Memento pattern and we could have a simple hash table object that we get values in and out of the object. This could work for all of the frameworks I listed. But, it seems cumbersome and still prone to major changes in the object topology. We're still depending on data, just putting a common interface on an object. Perhaps, this is the point of "Mirrors" in Self is to provide this type of functionality, albeit consistently. It's also slow because we have to keep taking snapshots if we want to track changes (which means we have to ask for the data and then do compares). But, we could use the Observer pattern and trigger events on any change to an object at the end of some change transaction. Another tool can be found in Allen Holub'sexcellent article about alternatives to getters/setters in GUIs using the Builder pattern. Now, the solution is very specific, but it's a turn on the Memento pattern. I like that fact that it's more about behavior where I have to send an object that understands the messages and reacts to those messages instead of being passive. But, it still seems the Memento pattern wins because it can be more generic (at its simplest: get(key) and put(key, value)). I'll leave this article as a point to ponder. There might not be an answer. But, I think a pure behavior approach is more understandable and simple than a reflective one. If anyone has any thoughts, please send them to me. I'll post any further thoughts as I have them. I'm always thinking of ways to preserve encapsulation in my designs. Just remember, Alan Kay wished he had called object-oriented programming, "message-oriented programming". It enforces the black box nature of objects and strong communication semantics. It keeps designs simple and easily reasoned about. Now, don't get me wrong, I don't hate reflection. I just feel like we should always seek alternatives, like inheritance it holds awesome power. But, in the wrong context can cause maintenance issues. Just because you have a tool doesn't mean you have to use it. Besides, forcing constraints on yourself, can cause interesting thoughts on your future designs. Labels: design, programming, self Comments
Saturday, January 27, 2007Smaller Is Better"The smaller the object, the more forgiving we can be when it misbehaves." -John Maeda, "The Laws of Simplicity" This quote was talking about real world objects, but I think it's doubly true for software objects. If we keep our objects small in our design (single responsbility), we will be more forgiving when errors arise. Why? Small objects are easy to test, debug, and well, fix. In fact, the fix is obvious because the object is so small. It's time to make our objects small and build layers of domain specific languages on top where each layer is simple to comprehend and understand. It would make our designs "more forgiving" to the ones who have to maintain it. Labels: design, programming Comments
Magnetic Fields Metaphor
Got this cool Alan Kay note:
Magnetic Fields: Metaphors are a hotly debated topic in XP/Agile circles and I've never understood why. Alan Kay's quote resonates with me because when you have a good metaphor for your system, you can easily make design decisions as they arise. It keeps your design cohesive and simple. Of course, if your metaphor doesn't fit, it can have the opposite effect. I don't force metaphors, but when one pops that fits...I grab it whole-heartedly. But, I will take the time to brainstorm for one. The metaphor should be your compass that helps you navigate through your design decisions. Labels: design, programming CommentsTuesday, January 09, 2007Zero Mass Design
First off, if anyone can get me a copy of Dave Thornburg's "Zero Mass Design", please send me an email as soon as possible. I was reading "Programmers At Work" again and this time I reading the interview with Scott Kim. And this was interesting to me:
He has a little book called "Zero Mass Design", the premise of which is that if you're going to work on something - for instance, you're going to write a book, or you want to write some software, or you are about to embark on any project that requires planning -- start with a simple design. But it's more extreme than just keeping it simple; you start with a design so simple that it won't work. That requires a great deal of discipline because you go into the project with the premise that you will fail; until you've tried something and actually seen it fail, yoiu don't know how simple you can get. Does it sound familiar? Sounds sort of agile doesn't it? The explanation that he gives next is the best I've read for "Do The Simplest Thing Possible" mantra of XP. Read on: Dave Thornburg's example, from James Adams' book "Conceptual Blockbusting", is the Mariner IV spacecraft. It had large solar-cell panels that unfolded. The problem, as stated, was to have a mechanism that slowed down the panels as they unfolded so that they wouldn't break when deployed. So, they tried oil, but that was sort of messy, and they tried springs; they did all sorts of things. The day for the launch was coming nearer and nearer. What were they going to do? Remember, the problem as stated was to find a way to slow down the panels, or to find a braking mechanism. Finally, somebody had the brilliant idea to try it with nothing, so they tried it and the panels shook and shivered but nothing broke. If you state the problem with assumptions, you're going to get them. You're got to pare back and pare back and pare back. Starting with a very simple design has wonderful advantages, but it requires a pyschological twist; you have to expect that it will fail and enjoy that. I like to get requirements stated in goals which is a trick I learned from use cases. Get the problem stated as a goal and a lot of assumptions can be removed. You'd be surprised how well it helps. The point is not only to "Do The Simplest Possible Thing That Will Work", but state the problem as "Simply As Possible". It removes assumptions and thus, doesn't color your design with needless complexity. Those words slapped me in the face and it seemed everything came together. Wow. Labels: design Comments
Sunday, January 07, 2007One Way
I was reading "Programmers At Work" this morning while giving platelets and ran across this quote from Jef Raskin:
Most computer designers, for some reason, delight in providing many ways of doing something. If there are fifteen ways to do something, they think it gives you freedom. The fact is that most users don't use the majority of the commands on their word processors. There are a few that everyone uses. And even though they've read the manual and know it might be a little more efficient to use a special technique, they don't bother. They use the same ones every time. I've seen this time and time again. I know I've even fallen for the "flexibility" fallacy where I should have been simple. The above can apply to not only user interfaces, but APIs and languages. It made me immediately think of Ruby. Ruby falls prey to the "flexibility" vulture and it's even touted as one of its greatest strengths by some (Reason #7). You have two ways to define blocks ({}, do), various ways of constructing arrays (new Array, %w), and other things to make various programmers from other languages feel comfortable. I think Ruby itself needs to become more opinionated. There's a lot of great things about Ruby that I'm proud that are finally getting into mainstream minds like dynamic typing, closures, and meta-programming. It's cool seeing all of the excitement. But, I think if Ruby wants to go further, it needs to trim some of the fat. Now, this fat might have made programmers from other languages more comfortable, but we need to lose it. I would like to see Ruby more lightweight, easier to parse (it's a nightmare), and of course more opinionated. But, I worry that with all of the activity that bad habits will continue. I'm worried about the future of Ruby because all of the forking and lack of a clear direction. With all of the battles that Ruby has won, I would hate to see it fail because of sins to gain more acceptance. It's won that, now, let's make Ruby the best it can be. Labels: design, programming, ruby Comments
Wednesday, January 03, 2007More On Nulls
Not to beat a dead horse, but Michael Feather's Offensive Coding article and Steve Riley's How null breaks polymorphism: or the problem with null: part 2 made me want to explain in further detail how I prevent using nulls. I thought they might be some use to other designers. Note, some of these are also discussed by Michael and Steve.
Empty Collections instead of null. The easiest way to prevent null pointer exceptions when using collections is to use empty ones. The Java collections library in fact includes empty versions of all the collection interfaces. Use them. It takes a little more typing, but it prevents one more check. And usually, most collection code can be written to be size agnostic. Use meaningful defaults. This one is really simple. Instead of being lazy, think about good defaults for your objects or at least ones that will be easy to spot when debugging. One of the problems that I have with nulls is that when I have to debug code, they tell me nothing about the intention. A default can give some sort of clue. If defaults can not be used, then... Throw an exception. But, if you must insist on using nulls, then please don't use them for incorrect usages of your API. In other words, if I pass you bad parameter, don't return null because your API can't handle it. Signal an exception and tell me what the problem is. Sadly, the Java collection API falls into this trap. The implementation of Map returns a null if it doesn't find the key and null is ambiguous in this case because it can be the key was not found or that null was stored as the value. I think an exception for the first case would prevent that. It would also make it easier to spot an incorrect usage of the API with a good message to tell me what to correct. Good designs are easy to debug and require little to no detective work. Be nice to your users. Use small objects. Wrap primitives into something more meaningful. You can then use the interface for both the implementation of real values and another one for true Undefined scenarios. Small objects are easy to debug because they can give much more meaningful information and you don't have to deal with all knowing objects that are 300+ lines of code. Small objects make dealing with nulls easier because... Null object Pattern. This pattern described in "Pattern Languages 3" by Bobby Wolfe. Go read it now if you haven't. Basically, implement a different implementation of your interface that either deals with the undefined case more gracefully. Now, by that, I mean, either throw an exception detailing why the request could not be completed, ignore the request, or forward it on to someone else. There could even be more options, but the first two are the most commonly used which of course, brings me to... Deaf object pattern. This is the Null object pattern where the implementation of the interface ignores all exceptions. This is used throughout Dolphin Smalltalk to great effect. This is a dangerous pattern, but when used right can make life easier. Dolphin uses it as a placeholder for objects that are not fully instantiated yet and requests would not make sense. It does this external resources where the object is instantiated, but they have not yet allocated the proper resources yet to make it fully functional. It helps them avoid timing issues. Again, I would caution the overzealous use of this pattern because it can mask bugs which brings me to... Undefined object pattern. Yet another popular variant of the Null object pattern, this version throws an exception with a meaningful message (be creative, but put something better than "undefined object<> In closing, the one point I want to make is relying less on null requires thought in your design. Too many times, I see use of nulls as absence of caring for the users of your code. Nulls are just lazy and you can replace most of the them in your code using the above arsenal. Plus, the techniques above can make your code more readable, easier to debug, and more fun to extend. Labels: design, programming CommentsMonday, January 01, 2007More Having Headaches Over Null
It seems I'm not the only who has heartburn over null in Java. I complained about this problem before and it seems a lot of people are finding the same headaches. The best medicine is to minimize your usage of it and be smart with your patterns (like Null Object, Deaf Object, and Value Object).
Labels: design, programming CommentsFriday, December 15, 2006Are We Worrying About The Wrong Things?
Frank Kelly has written an excellent article entitled:"Are we worrying about the wrong things?" It's mainly aimed at the Java community, but it has relevance to all of us. The points he makes are problems in any programming language (minus the closure and language debate ones). It's great reading.
I agree whole-heartily with him. After awhile, I get tired of the same old language wars (pick your tools, know them well, and kick booty). We all need to know our tools better. The core of the article is the message: Get better at your craft and mentor others on what you learn. It's up to us to make our community better. The time of being smug is over. We're in the same boat, let's help each other out. I love Smalltalk, but I'm not afraid to use other tools. And I really don't mind Java that much (most of my rants come looking at poorly written Java). Good design is good design no matter what language. Yet, I see under-abundance of talk on design. I'm still shocked at meeting people who have never read Rebecca Wirfs-Brock's excellent "Object Oriented Design" or "Structure and Interpretation of Computer Programming". Both excellent books on design that transcend language. If you don't have them, buy them now and get "Code Complete" as well. Anyway, the article is awesome and is a good wake-up call to us to look at the bigger picture. Labels: design, java, programming, smalltalk CommentsWednesday, December 13, 2006Thought And Readable Code
Chris makes the following observation while wondering why the functional folks are such zealots:
No tool solves every problem. Put down the damned hammer and pick up a screw-driver once in a while.I agree with the above sentiment. I love a lot of different languages for various reasons. I often say that everyday I get closer to becoming a functional programmer because that style keeps rearing its head in my code. But, like anything it needs to be tempered. It's all about balance and simplicity. I think the functional guidelines of trying to make most objects stateless and composition to the smallest and simplest are wonderful. The code you write reads well and if you try to restrict the places where you change, then it's easier to find bugs when problems arise. The point is don't go overboard with your ideology. The end goal should always be readable code. Readable code changes information in few places. Readable code is easier to maintain. Readable code performs well. Readable code cares about the programmer that has to pick up the pieces once you're on to something else. Readable code is small. And finally, readable code is simple. But, readable code takes thought. Design takes thought. Most of the zealotry that I witness is from people who want to remove thought. They think great code comes from restriction. It does not. It comes from freedom to have choice. Sure, you can make a bad choice, but sometimes the bad choice might be the good choice in certain situations. But, the choice requires thought. Any hard and fast rules should be looked upon with suspension. In the wise words of George Clinton: Think! It Ain't Illegal Yet! Oh, and if someone still wants to make religion and dogma out of anything technical (like the functional and XP evangelists), tell them to: Go Wiggle I'm going to think about making better code and designs. Labels: design, programming Comments |
My Weekly Top 20 Artists |
Comments
This post reminds me of the all-too-true quote by Brian Kernighan:
Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.
By
kesmit, at 9:51 PM
Hey! I wrote an entry about a clever coding too.
http://squeak.preeminent.org/blog/?p=325
- Steve
By
fastfingers, at 10:07 PM