October 15th, 2009 · 4 Comments
There are many ways to classify a programming style. One of the classifications I hear a lot at my current place of employment is Defensive Programming. Before I worked here I had noticed the style but I don’t think I had a word to describe the practice. Furthermore, if I had thought about a word to the describe the style I wouldn’t have called it Defensive. I would have called it Hide the Problem Programming. Before I get into my rant let me start by explaining the Defensive Programming philosophy.
Defensive programming is a form of defensive design intended to ensure the continuing function of a piece of software in spite of unforeseeable usage of said software. The idea can be viewed as reducing or eliminating the prospect of Murphy’s Law having effect. Defensive programming techniques are used especially when a piece of software could be misused mischievously or inadvertently to catastrophic effect.
Courtesy Wikipedia http://en.wikipedia.org/wiki/Defensive_programming
To be fair I don’t technically disagree with this practice, in fact I agree with it. However, defensive programming in practice (at least in my experience) is taken past what is described above to a very sad and ugly place. There are two things in particular that bother me:
- Unnecessary Code
- Null Checks
Let me break them down for you you.
Unnecessary Code
Most “Unnecessary Code” situations seem to be old habits that have been carried over from older languages. I would actually understand many of them in a more dynamic language where you have no compile time checks to ensure program validity, but I see them much more often in static languages like Java. This completely baffles me.
Would you ever do the following?
Integer one = 1;
Integer two = one + one;
if (two > one) {
return "the world is right: 2 > 1 is true";
}
If the answer is yes, please stop reading now because there is no way I could ever convince you of anything. If the answer is no, there is hope for you yet. This example is a bit extreme but it is a good starting point to demonstrate the ridiculousness of the practice. Now let me try a more realistic scenario:
public class Person {
private String name = null;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public Person newPerson(String name) {
Person person = new Person();
if (name != null) {
person.setName(name);
}
return person;
}
public static void main(String[] args) {
Person daniel = new Person("Daniel Roop");
}
This is a special case of a null check, which I will discuss next, but I see it all the time. Why are you checking for it to be null, if the default value is null? Why add the extra lines of code for no value at all. Which leads me to my number 1 issues with “defensive programming”.
Null checks
I would recommend to never get me started on this topic. I find null checks to be one of the most unnecessary, ridiculous, ugly practices in programming. It is right up there with Util classes. So let’s start with an example:
public void addFriendToList(List<Friend> friends, Friend newFriend) {
if (friends != null && newFriend != null) {
friends.add(newFriend);
}
}
This one isn’t obviously ridiculous. My problem with this scenario is that the person calling the method is requesting you add a friend to a list, but in the case where it provided incorrect inputs (like null parameters) it just won’t do anything. How is that helpful to anyone? Yes, you will avoid a null pointer if the friends list is not initialized but
- It should always be initialized, if not the code calling the method is screwed up and appropriate feedback should be provided.
- You should provide feedback when parameters are incorrect not hide the fact that something is wrong.
Taking this example to the extreme you might see something like this
public List<Friend> findFavoriteFriends(Person person) {
List<Friend> favoriteFriends = new ArrayList<Friend>();
if (person != null) {
List<Friend> friends = person.getFriends();
if (friends != null) {
for (Friend friend : friends) {
if (friend != null) {
if (friend.isFavorite()) {
favoriteFriends.add(friend);
}
}
}
}
}
return favoriteFriends;
}
public static void main(String[] args) {
Person person = PersonFactory.find("Daniel Roop");
List<Friend> favoriteFriends = findFavoriteFriends(person);
if (favoriteFriends != null && favoriteFriend.size() > 0) {
for (Friend friend : favoriteFriends) {
if (friend != null) {
System.out.println(friend.getName());
}
}
}
}
Why do I care? Does it hurt anything to have these extra checks? From a logic perspective the answer is no, but I would argue from a readability, correctness, and maintenance standpoint yes very much.
Readability
I believe code should be written in a way to express the intent through the source (not through commenting). Beside my wonderful method names, I don’t think you would have any clue what the method was attempting to do at first glance. Furthermore, even with those amazing method names, I would argue if you read the main method you couldn’t tell me what was going on.
Correctness
More importantly if something where to go wrong, and I actually did pass a null list I wouldn’t know. I would just get an empty list back and assume my person had no favorite friends. This is not the correct response, in fact it is the wrong response because I have many favorite friends (you the reader are of course one of them). There is no indication to me that there is anything wrong. I, the caller, received an empty list which is a perfectly valid response, but it is wrong.
Maintenance
This is really a combination of the two above. Imagine a large application (let’s say a photo sharing application) with this type of code all over the place. Now imagine you are given a defect that User A was not able to share an image with only their favorite friends. When they select only share with favorite friends no one can see the photos, but they can see their friends are flagged as favorite in their friend list. Where would you even start with that? If you hadn’t been so “defensive” you would probably have an exception written to the log because when User A request their friend list they get a null instead, thus causing a null pointer in the findFavoriteFriends method. This would not only give you a starting point for the bug, it would also trigger a notification of the problem before the User A complaint even reached the developers because your log monitor picked up the exception and notified the developers.
On the other side, you now don’t know if those null checks where put in place because they were meaningful, or just fluff. When I say meaningful I mean that a null value actually caries some information (e.g. null argument to a method means the parameter should not be used.) The developer implementing a fix on this method will have to assume that the null check has some meaning and work around those checks instead of improving the business logic
Loose Ends
I must admit the title of this article is a bit of a misnomer. When I began writing the article I agreed with the title, but after reading up on what Defensive Programming really is, I realized. I am a defensive programmer. Just not in the buzz word cop out sense. If you don’t believe me maybe you will believe Ward Cunningham who has a wiki article on Offensive Programming, and points out Defensive Programming is the same as a FailFast approach which is closer to how I would write my code.
Let me be clear, I am not suggesting you should show stack traces to your end user. But the fact of the matter is, something is wrong and often times I would imagine it is better to show nothing, then partial something. A good example of this is the recent twitter outage users were able to update their status, but there was no indication that no one could see the update. This caused a lot of confusion and I heard much of the interwebs (at least the small corner I pay attention to) say that it would much rather a fail whale than a semi-up system. What you should do instead is when there is an error with a request, catch it in your container (if it gets that far) and show a 500 message. Ideally logging the stack trace that will hopefully trigger a monitor to go off, and developers can investigate.
I can imagine this type of programming has it’s place, especially in embedded systems, but on the web they have no place. You have a simple request response, if one request doesn’t work oh well, display an error and ask the user to try again. Most of the time that will be better than sort-of working. And that is not to say there aren’t situations where non-mission critical pieces can be turned off if they don’t work, but the core functionality should not be written that way.
Conclusion
If I have convinced you of nothing else I hope I can get you to take away two things
- Think about every null check you implement, and ask yourself is this necessary?
- Stop calling what you are doing defensive programming, it is called “if I don’t see any errors there aren’t any errors programming” and it isn’t helpful to anyone.
No tag for this post.
Filed Under: development · programming · rant · theory
For the longest time I have thought I was crazy, because I just didn’t see the benefit of programming to an Interface. I agreed it was good from a design perspective, but as an implementation I saw no need to add the extra code for what amounted to zero benefit. I have finally come to the conclusion that a good chunk of people have absolutely lost their mind. I base that conclusion on the fact that I have found smart people that agree with me, to counter the smart people that lost their mind.
What am I talking about? I will cut straight to the point, when the GoF suggested programming to an interface not an implementation in their classic book
, I don’t think they meant a Java Interface. Mostly because the book was based on C++ and Java Interfaces weren’t around, but also because I think they are smarter than that.
Like so many things in the Java community, I think they took a really great idea and twisted it until it no longer made sense. I don’t think they do this on purpose, I believe they are just gluttons for theories. But, before I get to far into this rant, let me substantiate this claim with a few points of interest.
- Almost all languages at the time of the GoF publication did not have the notion of an interface as a first class citizen.
- The examples in the book were written in C++ which did not have the notion of an interface, although you can fake it.
- Patterns are supposed to be reusable bits of object oriented goodness, and since all object oriented languages don’t include interfaces as first class citizens it might not be essential to use these ideas.
Now let me switch into argument mode, I agree with the notion of programming to an interface. I admit that doesn’t sound like a very good argument, but hear me out. I will address each of the popular arguments individually, so what are they?
If you don’t use an Interface you are programming to the implementation not the interface.
So the point here is that you should be loosely coupling your classes in your application. When designing your systems you should always think about the interface (coupling) between your components and make sure they make sense. I firmly believe the best way to make a maintainable system is to create loose coupling and high cohesion as a wise man once taught me. But this can and is done in many languages, and at many shops without the need for the Java Interface.
So what am I saying? I am saying if I showed you the following code:
public class BankDelegate {
public void debit(Account account, Amount amount) {
account.debit(amount);
}
}
you would have no idea if account was an Interface, Abstract Class, or Concrete Class. And furthermore, I would suggest that it doesn’t matter. Let’s say my bank only has a single account type. This would work great. Now what happens when I need to add a second account type? I can either create a base class, that both my accounts extend from, or I can create an Interface. Either solution will work, and I don’t need to change the code in my delegate. Which means my interface has remained the same.
That is what it means to program to an interface. Because if I had originally wrote the BankDelegate class like this:
public class BankDelegate {
public void debit(CheckingAccount account, Amount amount) {
account.debit(amount);
}
}
Then my interface is only good for classes that are of type CheckingAccount. So let’s apply the same logic as before. I could write this as an Interface, Abstract Class, or Concrete Class and you would have no idea based on the above code. But of course, had I written it as an Interface it would be loosely coupled, and therefore I would be able to change it extremely easy at a later time…Oh wait, errrrr…It doesn’t make sense for a SavingsAccount to extend Checking account. I need to add a new base class or interface of Account and extend that. It will take the same amount of work to change this to accept a SavingsAccount regardless of what notation I used to create the CheckingAccount type.
Because of that I would argue that programming to an interface is more about thinking about the names of your classes and methods than using a special notation to declare them.
Why would you tie down your domain with unnecessary dependencies?
This is the argument that if I don’t create an interface, I will be importing unnecessary third party dependencies into my domain. The example I have seen suggests if you are creating a store, and you need to keep track of sales, you should create a SalesLedger interface. That way you can create a HibernateLedger that implements the SalesLedger, so as to not couple the Hibernate libraries to your domain. I agree with the idea of not coupling your domain with hibernate, but I don’t see how adding an Interface solves this. Even if I make the Interface I could make it rely on hibernate functionality, especially if it is the first and only implementation I do. Futhermore, the argument seems to suggest that I would have created the class HibernateLedger in place of SalesLedger if I had not created the Interface. That is probably true from the implementation standpoint, but form the interface standpoint, the methods would be identical to SalesLedger, the Interface. Am I missing something?
More importantly however, it is not unnecessary from a runtime perspective, which is essentially the only time you are ‘importing the dependency’ because without the Hibernate classes your code won’t work, no matter how many Interfaces you placed in between.
How else can you change the implementation in the future?
I am sorry, but this is ridiculous? Let’s break this down into what that really means.
1. If you mean changing it at runtime…that has more to do with the strategy pattern and your runtime environment, that a Java Interface. If you needed to change an implementation at runtime, you simply extend the original class, change the behavior you need, and inject your new object.
2. If you mean changing it for a future build…just re-write the code. Will you need the old code? Do you like keeping an archive of all your classes? If you need to change your SalesLedger to use a web service, instead of hibernate, go for it. You wouldn’t be using both at the same time.
So you ask, what if I need the old code in the future? I would say, isn’t that what version control is for.
What if we need to change the parameters in the future?
This is the one that I find most disheartening. The idea here is doing something like this
public void myMethod(IMyParameterInterface request) {
...
}
instead of this
public void myMethod(String param1, Integer param2) {
...
}
Talk about working around language features. What if you need to change the parameters in the future you ask? I don’t know change the parameters. I know, the response is, well then your interface has changed, and you need to go update everyone consuming that interface. Yeah, well guess what you just added a new parameter, that they probably need to send, and if they don’t, you can just overload the method and pass a null, which you would have to check for in your Interface based system as well.
So there you have it…
I recognize that Java Interfaces are essential to the language, because Java is a static typed language, and doesn’t allow multiple inheritance, Interfaces are the only way to create libraries like the collections API. I think it is unfortunate that the word ‘interface’ has been overloaded so much, that in this case it was taken literally becase and Interface type existed, instead of just treating it in the more generic term, of interface between two components or systems.
In conclusion, I hope I have enlightened, and annoyed a few people with this article. And maybe I don’t know what I am talking about, but just incase I haven’t been able to convince you, read this interview with Eric Gamma, I think you will find it enlightening.
Tags:
class,
code,
gof,
interface,
java,
java interface,
static
Filed Under: programming · rant · social · theory
I admit this is probably an obscure error, but I didn’t find anyone else talking about it. So, in an effort to help some other poor sap like me out…
If you receive this error in EasyMock 2.3
java.lang.NoSuchMethodError: org.easymock.internal.RecordState.(Lorg/easymock/internal/IMocksBehavior;)V
at org.easymock.internal.MocksControl.reset(MocksControl.java:62)
at org.easymock.internal.MocksControl.(MocksControl.java:26)
at org.easymock.classextension.internal.MocksClassControl.(MocksClassControl.java:19)
at org.easymock.classextension.EasyMock.createControl(EasyMock.java:108)
at org.easymock.classextension.EasyMock.createMock(EasyMock.java:46)
It is most likely because you have an older version of EasyMock in your classpath. In my case a referencing eclipse project had it, and in my project “order and export” tab I had the Project Higher on the Export list than the new version of EasyMock. the easy way to fix this is just bump the newer EasyMock jars ahead of the older ones, or remove the older jars.
Tags:
bug,
exception,
mock,
test
Filed Under: programming · troubleshoot
Passion. I have come to the conclusion that, that simple word is what separates an average developer from a great developer. IQ, education, degree, books, none of that matters, these are all symptoms of passion. A passionate developer will use these tools to find answers and refine his/her craft, so they are good indicators of a great developer, but not quantifiers.
A great developer takes ownership of his product, even if he doesn’t believe in the product. That person will most likely pour their life and soul into their current assignment. Typically you will find this person spending hours of their own researching and visiting user groups to enhance their skills. Some might even stop shaving facial hair in order to show their dedication to a particular assignment.
Should this be expected of all developers? No! Not everyone needs to have that level of passion for a project, but the chef needs it. I love the show Hell’s Kitchen and you can tell Gordon Ramsey takes a passion in what he does. Because of that passion he drives his potential employees to become better Chefs. This effect is exactly why the team leader needs to have this passion.
Time does not give you this passion, so in my opinion it doesn’t make sense to have the longest running employee be given top developer ranking. Sometimes age can push you in the opposite direction, and you no longer care about the right things anymore. Instead you care about trivial things like your dog, or your kids soccer game, or your birthday, or the big kicker sleep! I jest.
Why is passion so important? Is it because with time you care less? Not really. I believe this industry is extremely exhausting. Every time you turn around there is a new hot trend, and unless you are on it, you are so last minute! That is easy enough to remedy, just don’t get on the new train, then you don’t need the passion. The problem with that is that the industry is so young, if you don’t stay on the train you will probably be creating more work for yourself by following old practices that are more time consuming. This isn’t a problem in more established fields like Architecture (of the wood/cement type), or Dentistry (of the teeth type), because…they are established. These professions have been refined over hundreds if not thousands of years of the human experience. What? I don’t believe that statement myself, but think about it. Architecture has been around for literally thousands of years. Humans have had an extremely long amount of time to find out what works and what doesn’t. Programming has been around for 20+? There is a big difference there.
There is another aspect to the passion. Even after we have refined our craft to the point where it isn’t changing every 5 seconds, we will still need passionate developers. That is because programming is only about 20% of what we actually do for the organization we are apart of. We, developers, are responsible for building systems that satisfy business needs. There is no one size fits all here, we have to understand what the business needs in order to build it. That is what I love about what I do, because with every new job I can completely switch my entire industry, but be doing the same thing. So if I get bored with the travel industry, I can switch back to the financial industry, or I can switch to the military industry. But the flip side of that is I have to be passionate enough to invest the time and energy into understanding the business as my users understand the business. Otherwise you won’t be able to produce the highest quality product.
I imagine this is why 37Signals is so successful. They always talk about building applications for themselves. This is an easy way out, this allows them to bypass the second part of the passion, because they are the users, so they already understand what they want. This is not a bad approach, but everyone can’t do this. Could you imagine if doctors or lawyers had to write their own software, they would have to spend an additional 4+ years in school on top of their already 8+ years. They wouldn’t ever get anything done. I am not knocking 37Signals, I am just suggesting everyone can’t build software for themselves. Especially since it takes a certain mind to wrap around software engineering and it is rare to find a master of two trades that would be able to do this for every industry (not impossible, just improbable).
So what does it all mean? I believe it means that it doesn’t matter how fast you code, or how many patterns you know, but it is about the fact that you care about what you are doing. That probably seems obvious, but I personally find it extremely enlightening. Whenever I write a piece of code I think about why I did it, and ask if there is a better way. I don’t become attached to code, so when I learn a new technique I am not afraid to rewrite old code. I don’t see writing code as my job, I see building solutions to business problems as my job. The work is in figuring out what to build and how to build it, not the code that is making it happen.
This may be a bit presumptuous but I believe that is the difference between a great developer and an average developer. A great developer helps build solutions to business problems and needs, and an average developer just takes orders and writes code. All the work is in working with the business to figure out what they want and how to do it, to a great developer writing code is like polishing the hardwood floors, it is the cherry on top of the perfect ice cream sundae.
Tags:
career,
passion,
work
Filed Under: development · programming · rant · software
I would have to assume that there are plenty of developers out there that want to install JRuby and Ruby on the same machine. There is a tragic flaw with this, the gem command is the same for both. If you add both to the path there is no way to distinguish between the two version, so by default the most recent gem command included on the path will be used. I have come up with a solution that I am happy with to solve this problem so I thought I would share it with the world.
First I am on a Mac, but this should work on any *nix based system. Also I am using bsh, so if you use a different shell make sure to adjust the setting for that shell.
The process is quite simple, download/install Ruby and JRuby. There are numerous tutorials for getting Ruby installed I would recommend the install guide put out by Dan Benjamin at Hive logic. I typically tweak Dan’s tutorials and put my binaries in their own directory. So instead of
/usr/local
I would install the binaries to
/usr/local/ruby/ruby-1.8.4
Then I would make a symlink from /usr/local/ruby/current to the most recent binary.
cd /usr/local/ruby
ln -s /usr/local/ruby/ruby-1.8.4 current
And add /usr/local/ruby/current to my path.
RUBY_PATH="/usr/local/ruby/current"
export RUBY_PATH
PATH="${RUBY_PATH}/bin:${PATH}"
export PATH
I would typically add these lines to my ~/.bash_profile script to be executed whenever my shell starts.
All is fine and dandy until I install JRuby. To install JRuby simply download the latest flavor. JRuby 1.1 was just released so I will use that as my example. Unpack the tar into /usr/local/jruby/jruby-1.1. Now repeat the step above for creating a symlink to /usr/local/jruby/current.
cd /usr/local/jruby
ln -s /usr/local/jruby/jruby-1.1 current
Now here is the first tricky part. Because I want ruby to be my default command line ruby executable I don’t want to add JRuby to the path. I do however, add the JRUBY_HOME environment variable. To do this simply add the following lines to your ~/.bash_profile script.
JRUBY_HOME="/usr/local/jruby/current"
export JRUBY_HOME
Now I need to create a way to add JRuby to the path as I need it. To do this I created a script and placed it in a folder located in my User folder
~/Scripts
I named the script load_jruby but you could name the file whatever you like. In the file I placed the following contents
PATH="${JRUBY_HOME}/bin:${PATH}"
export PATH
The final piece to the equation is called sourcing the script. This basically means you want to run the script in the current process. You need to do this because by default when you execute a script from the command line it spawns a new thread and any environment variables you change would only be affected inside of that script. To source the script you simply prefix the command with a “.”. So instead of
~/Scripts/load_jruby
you would type
. ~/Scripts/load_jruby
Now if you type
jruby --version
You should get output describing the version of JRuby you are running.
Obviously if you would prefer to have JRuby be your default Ruby installation just reverse the instructions. Also this will not affect any defaults inside of IDEs like Aptana, so make sure you set those up for their respective environments.
There you have it, you can now have Ruby and JRuby installed on the same machine and work with them independently from the command line. Whenever you want to work with Ruby open a new terminal and start typing. If you want to work with JRuby open a new terminal and run your script to start rocking the JRuby.
Tags:
bash,
bsh,
gem,
ide,
jruby,
mac,
path,
ruby,
script,
Scripts,
shell,
symlink
Filed Under: development · languages · programming
March 12th, 2008 · 1 Comment
I have recently become frustrated with the Eclipse IDE. I use 3.2 to do my java development, Flex Builder to do Flex development and Aptana RadRails for Ruby and Rails stuff. This last week I almost cracked and purchased TextMate , to see what all the fuss was, but since Aptana just released 1.0 of their RadRails plugin I figured I would give them a little more time.
I don’t tend to use most of the IDE features, highlighting and formatting are the big things for me, but code insight is always a plus. I will give RadRails a few weeks, or until another great bundle that makes TextMate super cheap and make my decision then.
Tags:
ide,
java,
radrails,
rails,
ruby,
textmate
Filed Under: development · software
Michael, Bucky and I have cranked out another 2nd Player Episode. We cover where we have been, and plans we have for the site and podcast. We are not sure if we are going to stick with the bi-weekly schedule we had before, but we are going to attempt to be more regular than we have been recent. Any comments are welcome as always, thanks for listening.
Tags:
2ndPlayer,
podcast
Filed Under: gaming · project
February 20th, 2008 · 3 Comments
For some reason, I decided to upgrade WordPress to 2.3.3 tonight. In the process I decided my layout was a little dated, so I updated that too. As an added bonus, the RSS Widget began working, I guess it didn’t work on the old WordPress engine for some reason.
I even took the time to tweak the layout a little to include tags and categories, as well as updated the header image to be one of my own. You are looking (if you are on my site and not reading this in your rss reader) at Lake Eola at Downtown Orlando.
In completely unrelated news, I put together an Infant T-Shirt for my son at CafePress in honor of TallyHoh launching.
Tags:
layout,
rss,
tallyhoh,
wordpress
Filed Under: software
One of the things Tyler and I discussed doing to make the user experience with TallyHoh a little better, was to create a bookmarklet. I had never done this before so I did a little research to see what was currently out there. I knew del.icio.us had an active bookmarklet community so I started there.
What I found was pretty amazing, everything from bookmarklets that simply redirected you to delicious, to full blown javascript rendered forms that allow you to tag your feeds as they are submitted. After taking all that in I decided to start with a simple redirect.
At first everything went great, unfortunately I needed to do a post, and the examples I saw didn’t suffice. So putting on my javascript hat, I came up with the idea to create a form via javascript and submit the form. I realize I am not the first to come to this conclusion, I was just excited that I thought of it. This ended up working like a charm, and what I ended up with was this.
Add Feed To TallyHoh
If you drag that link into your toolbar you will have all the power of TallyHoh at your fingertips. Well at least the ability to add feeds and subscribe to them. Basically what happens is this crafts a request to add a feed to TallyHoh, if you are logged in (via remember me) it will automatically subscribe you to the feed. If you are not logged in, it will explain that you are a chump and should log in, and allow you to subscribe at that point.
Overall I was pleased with how it turned out. My first bookmarklet went off without a hitch. Feel free to try it out and let me know what you think. Eventually this will live somewhere on TallyHoh but Tyler and I are still figuring out where.
Tags:
bookmarklet,
javascript,
tallyhoh
Filed Under: project
February 18th, 2008 · 2 Comments
You have to read the title as if your are Lion-O from ThunderCats. You remember…”Thunder, Thunder, ThunderCats…HO!!!!!”. The new product that Tyler and I are pushing out has nothing to do with ThunderCats, that is unless you subscribe to a ThunderCat News Feed like Thundercats The Movie.
Without further ado…
You have heard me talk about it, and now the wait is over! Tyler and I (mostly Tyler) have been working to prepare our beta initial release of TallyHoh. We officially launched it today and are taking the Better, Not Beta. We have some updates we plan to do with the UI, but we want to see if people actually like the product. I have been using our builds for the last 9 months now, and I couldn’t live without it. The end goal is to provide a place for you to aggregate all your feeds (RSS Reader) with the functionality to share and express your opinion about the content you are reading (Social) to other users (tally = positive, folly = negative, nully = don’t care). We are attempting to implement as many web standards as we think makes= sense, hReview tags for our review sections, OpenID for our authentication, OPML for importing and exporting (boo) your subscription list and RSS and ATOM for our syndication methods. You may notice a friends feature missing when you sign up. This is mainly because we are hoping a project like DiSo will gain enough momentum or at least get to a point where we can use the standards they produce as an open friend network so we don’t have to recreate the wheel for the 1 Billionth time.
Thanks to everyone who takes the time to try out our first creation. And to all those who don’t…well thanks for reading my blog ;-). If you have any issues or comments at all please don’t hesitate to email me (daniel [at] protoh dot com).
Tags:
daniel,
openid,
protoh,
tallyhoh
Filed Under: social · software