I have changed my job 6 months ago to adapt to changes in my private life. I am still big fan of previous employer. Recently I started to use their product/service that was in early stage when I was leaving. Well, I used their other product while being there, so being their customer should not be new for me, but that one was stable and I've been in while this one is still in beta and I see from outside. I did not participate in development of either of them.
As can be expected this new product has issues. Bugs, usability problems, unfinished features. I expected that and it is not the part of problem why I decided to write about that. I always send my bug reports and suggestions. They are usually answered soon. Problem is when the answer contains "I asked and it should work". So it was not tried after my report, it was just believed it (still) works - maybe because it takes less time to assume than check and they have a lot of other things to do. But how long does it take to check that RSS feed does not contain any content? 20 seconds or less I guess. My point is that experience have brought me completely new perspective. Till now I was always at provider's side and now I got to other side. Seeing different steps taken than I hoped for as a customer/user opened my eyes. Issues are not bad, only that having no clue when and if they will be resolved feels annoying. The service is free, so I do not feel ripped off and I understand their priorities have to be on paid tasks, but still it feels annoying. My current employer has better approach to handle these situations: acknowledge and thank for bringing to attention, assess, apologize and say when it is is expected to be fixed (followed by fixing it, of course). Taking this path is not only helping to keep good relationships between vendor and customers, but in fact can be felt as rewarding to customers, because they can be proud they helped with product/service. For me, it means that I am now aware of these problems and I hope I will remember them next time I'll be assigned a bug.I am big fan of two programs. Big means that I bought also upgrade. It also means I used to advocate both of them. This is not the case with one anymore. These products and their companies have many common things and many differences. Let's look at them both.
Similarities:
- Both products are excelent in what they offer, they have many unique features
- Both are commercial products surrounded by commercial and free competitors
- Both companies need to release new version every year to keep income
- Both companies need about half a year for finalizing public version to finished state (see later!)
- Both companies have a lot of bugs reported by customers which is normal (see later!)
- Both companies need to balance enhancing existing features vs. adding new
- I resist upgrading to new version of both products until some killer feature is added I must have
- Both products satisfy my needs in their respective area
- I have bought at least one upgrade of both of them in past
- Both companies distribute their products mainly through web (see later)
- Both companies have associated discussion forum for their products (see later)
- Features of both products are copied by competitors
- Both products need a lot of computing resources and are a bit slow (that means if they were twice as fast, I would be glad)
- JetBrains starts with Early Access Program that can be used by owners of previous version for free, getting valuable feedback about problems and features driving final great product. DXO releases final (paid) version that is completely useless till version x.2
- JetBrains' developers are active in discussion forums trying to help, DXO ignores bug reports and tries to blame customers (indirectly for buying their terrible product!)
- When JetBrains releases new version it contains change list (detailed for EAP, somehow more generalized for update). DXO is able to release several updates without saying what was changed.
- If you buy new computer, update version of Java or switch to another language/library, there is fair chance you still can use your version of Idea. If you buy new camera you will probably need to buy new version of Optics Pro. Or if your new camera is supported by existing version of Optics Pro it is possible your existing lenses are not. And upgrading might not help you.
- I have seen JetBrains' server down, but DXO is master in this area. Many people are not able to download product/update, downloaded file is corrupted or even users are not able to activate their license after they paid for it. It can take days to be running again.
- Some people (former users of Optics Pro) are coming to forum for fun - to see how other more tolerant people struggle to use Optics Pro and are abused by DXO
- JetBrains care about their customers, DXO does not
- JetBrains care about their image, DXO does not (I am sure their management is not stupid, thus this conclusion is only one that I could make)
Am I going to buy upgrade of these products when they are finished?
- JetBrains: I am already using their 8.0 Milestone 1, hence the answer is probably yes.
- DXO: I am afraid to install their latest update 5.2.1 because nobody knows what was changed and there are long discussions about broken reading on RAW files. Nobody knows what will be offered by 6.0 and I hope I will not buy a new camera supported only by 6.0 if I still want to use my version of Optics Pro.
Why I preffer attitude of JetBrains?
- By releasing EAP soon and often people can try it to see if it works for them. As software developers they are usually striving for new features and they get them. Basically they get addicted to new version long before it is done!
- Released EAP has usually lot of bugs, but people do not feel robbed compared to what happens after paying for Optics Pro that is full of bugs (and refuses to start). Nobody will bash JetBrains, because the newest build (Release Candidate or even less) has 1234 bugs, but there are not many phlegmatics that would be satisfied with wonderful marketing materials promising something it cannot deliver 10 months after paying for product, because it is unstable and full of bugs. (To be fair, I waited patiently 8 months since 5.0 was shipped and bought 5.1 and I have never experienced the worst things described in forums. I consider myself lucky).
Why I wrote this
Now I am getting to reason why I am writing this. My intention is not to mock or ruin business for DXO. They are self sufficient it this area. Contrary, I try to help them in only way I can. Please, DXO, start to behave as a comercial company that plans to stay in business and start to do what your customers deserve from you. I have given you one example worth of following. Oh, there is one positive twist about all this - I am really happy I did not encourage anybody to buy Optics Pro 5.x. I would not be able to apologise enough for that!Does your build system allow you to depend on tests from different modules?
Added: August 20, 2008
Recently I have created 2 tests for two classes that looked very similar. Well, they were same apart for testing different subclasses of the same parent. Indeed, tested classes were also similar. I have to fix this (on both sides if possible).
I have found this problem when I wanted to introduce new method to interface and was adding test for it. So my first idea was to reuse test implementation for both classes by creating abstract test and two subclasses handling different initialization of tested classes (different class to instantiate and also different colaborators). Problem is that these two classes together with their tests are not located in the same module. Natural place for abstract test is to put it into module that contains interface, because it is basically testing contract of that interface. Then add concrete tests into modules that implement interface. However our build system does not allow me to do that because tests are not part of built artefact, thus dependent modules do not see them. A workaround for this problem is to introduce a new module that will contain abstract test masked as production class, hence it will be visible from other modules. Of course, this new helper module will not be included in installer of product. All problems solved... Not so fast, theoretically this works perfectly, but it might not be practical if that new module would contain only 1 or 2 abstract tests. It is heavy weight solution for such amount of code. Our codebase contains tens of small modules separating different parts of product to abstract and concrete modules (which is good) and introduction of test-supporting modules might help this number to grow rapidly. Fortunatelly I have found that I can solve this particular problem differently this time, because only occured due to badly factored production classes. If I create abstract superclass for them, I can easily remove duplication of tests and add new test classes testing only logic that is different. Somehow I am getting different moral conclusion that I planned with this post - "Look for real source of problems before attempting to fix them", but that's only because my case was special. In general, it is important to "Make sure your build system allows you to depend on tests from different modules" (only tests should depend, not production code). This allows you to do better tests regarding removing duplication by factoring common code to helpers and introduction of abstract interface tests.Note about testing of intefaces
I was not able to google good example of inteface tests (Apart from that you can read about them in JUnit Recipes by J. B. Rainsberger, but that will not help you to get up to speed without having the book at hand. Still I encourage you to read it later.). So I am going to write a few lines about the idea. Let's have interface Stack.public interface Stack { void push(Object data); Object pop(); boolean isEmpty(); }This interface has defined contract. It can be specified as Javadoc or any other way, but how can you make sure that all implementations comply to it?
The best way to achieve compliance is to provide abstract test case that implementor extends and provides method for creation of tested instance. Then she can execute test and everybody knows if it is compliant or not in fraction of second.
public abstract class StackTest { protected abstract Stack createInstance(); public void testShouldPopLastPushedItem() { Stack tested = createInstance(); //... rest of test } public void testShouldThrowExceptionWhenPoppingFromEmpty() { Stack tested = createInstance(); //... rest of test } }Having these tests for contract it is quite easy to verify our cool implementation of
Stack
is working:
public class PersistentAndsSecureStackTest extends StackTest { // add some setUp and tearDown if needed protected Stack createInstance() { return new PersistentAndsSecureStack(); } }
Back to the point
This is only one of the reasons why my tests need to be able to depend on tests from other modules. It is simply strong tool for making sure product is well tested and all duplication is removed.I am still not able to set up dependency of tests between different modules. Fortunatelly I did not need it again.
Recently I was trying to help my younger colleague with preparation of list of topics what he should learn. Well, it was my idea to provide such a list to him, I am not really sure if he was really interested in short term as his highest priority now is Java certification. My list included following:
- OOP
- design (principles)
- design patterns
- TDD
- refactoring and how to identify code smells
- writing expressive code
Then I've realized it is huge task list as none of those topics can be learned in "a week". Even worse, I am not able to prioritize them to help tackle this daunting task. My best offer is along the lines "learn everything at the same time".
I have to say I am really glad not being in the same position, that I need to learn everything again. It hurts just to imagine what I would need to learn. Of course, I still need to grow in all areas (as most of us), but it is easier to select some topics that interest me more just now and go through them without a rush. Or to jump from one topic to another.
Confuse majority of your customers to help a minority
Added: August 19, 2008
Recently I have logged into internet banking application of my bank in Slovakia. I found an ad offering me a loan. That is fine, I like to be informed about potentially useful things. This time, however, I was not interested. But I could continue only with two paths "Contact me, I am interested" and "Show me this offer later". This looked like unintentional omission of third possibility "Thank you, do not show me this again". So I contacted the bank and told them about this.
I've got reply next day (yes, they have good customer service). From the answer I have learned that that option was there before, but sometimes people clicked "I do not care" and then, some time later they wanted to apply. So the bank decided to solve this glitch. And they did. Problem is, that solution seems worse than original problem. People not interested in that offer in current version of internet banking are supposed to click "Contact me, I am interested", that will present them a form they need to fill out. The critical part is to leave the form empty. That handles missing state "Thank you, do not show me this again" with possibility of changing your mind later, because empty form will be available in "empty forms" folder. Huh? How was I supposed to figure that out? And what about people that do not keep in their mind that you need to click Start if you want to shutdown Windows? I do not know what designers/programmers in my bank were thinking about, but I expect I am not the only person who would not click "Contact me" option if I do not want to be contacted. It seems to me that someone suggested the cheapest technical solution how to add new this new functionality - just remove existing feature and existing code will handle rest of it. I am sure they did not think about people using their product.I hope it is only workaround until they implement, test and deploy working solution.
I started to develop my Photoblog about a half year ago. In the beginning I just wanted to try Rails and the best fit was to start with photo publishing application that I needed at that time. It was killing two birds with one stone, because I was not aware of any existing blogging application that would fit my needs. I wanted to publish many photos associated with prose-like text so galleries were out of question and blogging applications I've tried were clumsy regarding uploading many photos. But maybe the biggest excuse to do it myself was Rails :-) In the beginning I did not use any VCS. List of excuses contains following:
- I am just playing with Rails
- I am lazy to learn how to install Subversion on Win or force my brother that hosts the application to install it to FreeBSD.
- app is not big enough to deserve VCS
- fear of breaking it with too many changes and not being able to get back
- all changes need to be small in order to avoid mentioned fear
- (never needed for this blog, but I do it often in work) no history for artefacts. If something looks strange I like to check history to see how I got there
- once deployed, unable to get back easily. You can imagine how often my production environment caused previously unseen problem. When that happened I needed to work fast to resolve it in production, because it was often easier that manual rollback to previous state
- revert in case of messed code, wasting only time it took me to mess it, but not more.
- explore new features without a need to finish previous one - I started to do gallery-like navigator in parallel with changes in layout, exploring different layouts and finally started to implement this IT blog, gallery and more. When I am happy with some feature and I like to move it to production, I just push to to main repository and merge.
- do very easy rollback in production if I find "it will not be as easy as I thought to put it into production, but I do not have time to finish it immediately" (it might need keeping migrations correct and tested, but otherwise it is matter of
hg revert 53
- hg clone source dest
- hg status
- hg ci
- hg up
- hg push
- hg revert
What is the most useless piece of unit test you have seen?
Added: August 17, 2008
When I was thinking what should be my first post in my new professional blog I have remembered test method (in one undisclosed code base) I have seen a few months ago. It was test for equals()
. Rather amusing one, clearly not doing what it was intended for. (I have changed it a bit to protect author and I should state that it was written years ago, thus its author does not do these things anymore).
public final void testEqualsObject() { final SomeObject objOne = // new ... final SomeObject objTwo = // new ... final SomeObject tmpObj = objOne; assertTrue("The equals() failed", objOne.equals(tmpObj)); assertFalse("The equals() failed", objOne.equals(objTwo)); }That
assertTrue
can test only default implementation of Object.equals
that uses ==
, but nothing more. assertFalse
is a bit better, but not enough for quite complicated contract equals
and hashCode
have.
I have fixed it, unfortunatelly not in way I would like to - by usage of EqualsTester, because this and all other implementations of equals()
use
instanceof
that is controversial enough to start fighting about right now.
Do me a favor, any time you need to test equals
and hashCode
use EqualsTester
, please. You will not regret it in the long term.
What is the most useless piece of unit test you have seen? Your code done in past counts too ;-) How did you fix it?