![]() |
Tags
|
|
September 3rd, 2006
There are millions of programmers and trillons of lines of code out there that use Java, but as Java is an All-purpose language, most of the code produced is procedural redump of what people did when they used C or Fortran.
I came up with a short-list of sanity checks that I use to benchmark my designs.
- No public method shall return null, this applies also to protected methods of non-final classes
- Never pass this as an argument
- Getters and Setters are evil
- A public method shall be declared by an interface (sometimes an abstract class is sufficient and more convenient)
- No switch / if-else if on class invariants (this includes instanceof)
- A class shall be either (conceptually)final or abstract
- No method declares a foreign exception (I am OK with some checked exceptions from the java* packages)
- An object that can be passed back to the API shall be immutable or a Builder
- Prefer delegation over inheritance (this makes 6. reasonable)
- The class skeleton shall fit on a single page, as do all methods (including the private methods that are used by a single method only)
- The number of ctors shall be much smaller than the number of instance methods
- All statics are in enums
This is no style-guide (most of them are a waste of time with as little impact as knowledge is required to contribute), this is what I distilled out of code that pleases me and that really works and has been proven to be maintainable and easy to integrate.
To be continued / comments and suggestions are welcome.
Actually I know of style-guide that focussed on more than indentation and the placement of whitespace: JSF air vehicle C++ coding standards
Update: I started some explainatory notes on each topic (1-6,10,11 for the moment) under Design Rules
Posted in
Architecture, Java
7 Comments »
August 21st, 2006
After some assembler (8080!), the first programming language I learned had been Turbo-Pascal. If I remember correctly; it could not have been Turbo, as I was 12 years old at that time and Turbo did not yet exist then - well it does matter that much - I definitely used Turbo-Pascal later.
Borland seemed to find the name “Turbo” not very professional, so they dropped it. As IDEs are now a commodity, the professional touch is gone and the Turbo is (nearly) back!
As C# has some appeal to me, it looks like I could use another creation of Anders Hjelsberg…
Posted in
Architecture, Java
No Comments »
August 19th, 2006
Dushan Hanuska posted an interesting claim:
One of the symptoms of object-oriented programming is the lack of switch or case statements
I’d prefer to say (OK, I am not a native english speaker, so perhaps I didn’t get the original intention)
One of the features of object-oriented programming is the absence of switch or case statements
His article shows ploymorphism at work while he is refactoring a simple class. In the end he comes up with 4 classes and one interface, well I am fine with that, but I know my co-workers, they don’t like it.
Nevertheless, the best part is at the end: He nicely lists in order the standard refactoring procedures applied to clean up the code. So read to the end, perhaps it is something you should do also when defining design guidelines: Showing how “old-school”-solution can be transformed using refactorings (thus preserving the functionality) might be more instructive than pages of prose and fuzzy reasoning on how some problem should be solved. This problem is not a concern for the most of us, but when you have to integrate (experienced) developers from a different background it could be a help.
Posted in
Architecture, Java
No Comments »
July 13th, 2006
Design-time
This time it is a terrible implementation of a even worse API to do pseudo-XML. I spare you the details - only this: If you want to use XML use it correctly, and if you can’t: Don’t try using standard-conformant XML-libraries like Xerces.
Why doing such a thing at all? Actually this is more a comparision between C++ and Java design problems. On the Java side you can also get into problems like that, for example when you want to wrap XML-handling so that a J# implementation can use its beloved MSXML.
The C++ code was a bright example what happens to your code if you think you don’t have time for refactoring. Originally it had been something to access a proprietary text-format. Then some bright mind seemed to have the idea that use of XML is imperative. He handed this over to some Windows-programmer that cranked out an equivalent interface using MSXML. Eventually the got got ported to UNIX and Xerces as a parser, as cheese topping Unicode support had been added. Remember: No Refactoring!
Something had to be done. But actually it got more complicated as I thought. I specified some getters:
int getInt();
long getLong();
…
string getString();
Stop, my colleage said: “Writing
void getString(string&);
gives much faster code”. I could relate to his argument, but I simply don’t like out-parameters, our C++ architect was with me, but my colleague insisted on benchmarking, and the benchmark show an overhead of about 15%…
I felt that I had to give in. Well, he was outnumbered and showed that he would trash the implementation, if he was forced to continue implementing this API. The remedy was that we settled for:
operator int() const;
…
operator string() const;
Of course as slow as the get, but a bit more obscure - list-access we get in groovy-style via operator[](int) - so the developer is happy, he traded performance for something else.
So we will waste some time at runtime…
In Java we wouldn’t have a had a choice. And this is gooood! Really. No irony attached.
I implemented something similar in Java in about an afternoon and it was all straightforward. Two minor bugs which got corrected in seconds.
The whole discussion above took actually two days with three engineers involved. That is a waste of time. As this all came up shortly before I wanted to leave for vacation, I got sometimes a bit impatient by stating that this all wouldn’t even be a topic in a Java implementation. The response from the C++ folk was: “Well, C++ programmers have to be smarter to avoid stupid code”.
Well, I think using a more development efficient language is at least as smart because it avoids wasting time on stupid little problems.
A final word for the performance obsessed: The code in question will be used to access a remote interface, on both ends there will be heavy database-access. No profiler will ever list one of the functions mentioned above.
Posted in
Life, Architecture, Java
No Comments »
June 17th, 2006
Before getting up this morning I reached out for an issue of Wired magazine from the pile under my bed.
I read an article on a training facility for US-soldiers going to Iraq; though totally unrelated to software one thing struck me: While praticing the soldiers uses laser-equipment to simulate bullets. As a drywall or any other non-transparent material block light, the soldiers train themselves to simply hide instead of going for cover.
Association chain: cover - coverage -tests, well here we are.
Back to the picture, as a seasoned CS-player I know the difference from first hand (unlike real soldiers I am still able to talk about it). Software is not far off from a game; if you introduce a bug, you can correct it later (errm not in all cases), well at least if your real-estate site crashes, no one get killed.
With test cases it is sometimes the same, they assure you that everything is OK, you might have 98% coverage and all tests pass. Then after release a bug comes from nowhere and takes the system down. Where do these killers from? While you have perhaps validated each single function in isolation, you will never have any reasonable coverage of all possible scenarios that execute them in sequence.
My favorite bug of this kind could be reproduced in the following minimal scenario:
- Insurance contract live for at minimum 15 years (this implied the 15*12*6 ~ 1000 batch tasks had been applied in the background)
- 3 uncommon alterations made to the contract where the 2nd got canceled and redone in this period
After I found out what went wrong I had the greatest respect for the tester who conceived this test-case, - until he confessed that it came from a standard scenerio where he made two(!) errors as he went through it.
In fact the bug came from design flaw, for the fix I had to rewrite large portions of the sequencer (>400 classes). Irony here: The sequencer had been one of the few components where we had unit test (It was abck in these C++ days where things like JUnit just came out). Design flaws are rarely found by test-cases as the tests follow the design (unit tests) or duplicate the design (scenarios).
As any design element comes down to some code to implement it, one might think that there could be a test for it. Unfortunatly most of these tests are just “drywall”, they assure you that “covered” your code, but they guard you only against “visible” bugs, the paradox is that the design-elements have to take care of themselves because thats what they had been design for in first place.
The paradox continues, the code that manages to hide itself completly seems to be impregnable, mostly because they are protected by a concrete wall of design-elements that protect it from getting unexpected data ( in my case the actual implementations of the commands in the sequencer where all fine).
Morale: Test-drive code where you can do it meaningfully, design carefully code that will be hard to test.
Posted in
Architecture, Java
No Comments »
May 20th, 2006
I looked over Martin Fowler’s note on the kinds of code ownership, well don’t expect much from it, but it is a nice definition of terms to be used in discussions.
This morning I read a followup that shed a different light on it.
In the original post social factors had been identified as the driving force, where the followup also mentions the focussing on competence is relevant.
That fits a bit more accuratly into my experiences, while I like to throw in a thrird factor: organizational structure. Collaborative ownership across departments with different goals/deadlines are almost guaranteed to make a mess, especially if there a located in different parts of a building - or even worse - in different countries.
A working social network is an enabler, the presence of know-kow differences and organizational borders are pure disablers.
So if we strive for agility, we have to strengthen the enablers and eliminate the disablers. Though it is possible to overcome most of the obstacles, sometimes it isn’t or it is simply out of your hands. In these situations that are probably the majority of the cases when it comes to large projects and/or big (or those who think they are) organisations, you have to adapt your project to suceed. Read the rest of this entry »
Posted in
Architecture, Java
No Comments »
May 20th, 2006
Just finished reading this book, all in all a great read. Especially the first six chapters (when it comes to things like debugging it gets a bit shallow)
Two practices, Feeding Agility - Learn when to Unlearn and Delivering User what they want - Justify Technology Use caught my attention in ther combination.
In short what they are talking about:
Learn when to Unlearn
Every technology has its time - some techniques are not useful anymore or got replaced by something that suits today environments better. This ranges from programming languages (indispensable techniques from C/C++ are questionable in a Java project and perhaps simply idiotic in the realm of a dynamic language) to the interface technology (3270 terminals, GUIs and webacces have different constraints and advantages which you cannot exploit in all the same way).
Justify Technology Use
Here they coined the nice term “résumé driven design”. It denotes the choice of technology that serves primarily the personal interest. In fact this goes even deeper down: Pathological use of patterns leads to same problem. Resources get wasted to attain a goal that has nothing to do with the goals of the project.
Each practice described in the book is rounded up with a section Keeping Your Balance: Real-life is not binary; you have to evaluate in your very situation to which degree it makes sense to apply a practice.
The two practices above with their balancing form a teeter-tatter. You can make it swing by leaning forward-backward on the opposite sides of it - thus destroying the balance. New technology and new techniques are sometimes antipodal to things you already know and use.
This means that you can stay globally in balance if you under- or overdo both practices - although that is not supposed to be 100% healthy. More important though: You always have to keep global balance. When dealing with stuff that’s right out of the oven, you have to question your habits thoroughly to apply them successfully.
The main problem here is that technologies seem to be easier to learn than habits can get changed - I do no claim to be an exception - I observed how hard I struggled with (G|Ruby on)rails; I had to throw some views over board to make progress (the annoying thing was that it eventually turned out to be a déjà vu and I#d to fall bach to techniques I unlearned several years ago).
As my first boss said: If it was easy we wouldn’t be here in first place.
Posted in
Architecture, Java
No Comments »
April 23rd, 2006
This is somewhat a follow up to my blog on (non-reusable) components. I advocated there small components with simple interfaces that get tied together to implement the actual feature.
On DI and how to implement this much had been written, so the composition and the necessary techniques are common.
The communication among the components is less stamdardized and becomes quite a problem when dealing with many components.
The component technology du jour is SOA. SOA is using BPEL in an Orchestration manager to define and execute a complex operation. As a backbone of SOA WebServices get mentioned the most (there are other options). So what the orchestration-manager practically does is sending SOAP-Messages to different machines and collecting the results, to form new call and so on. When you keep all messages send, you can replay them eventually to duplicate the process.
This is very interesting for the analysis of errors. A complex business-function uses millions of lines of code and you have to have a tool to replay the action to see what happened and what exactly went wrong. The hardest bugs are the phantoms, the bugs you cannot reproduce but keep popping up at you customer. Read the rest of this entry »
Posted in
Architecture
No Comments »
April 23rd, 2006
As I mentioned ealier (perhaps not here), writing unit-tests is cool.
Unit-tests are fine because they encode you knowledge about your system-components in a formal and executable manner.
The basic idea of TDD (Test-Driven-Developmement) is to translated the specification into tests and start programming until all tests will pass. This can lead to some pathological results, of course. You add features to you system by defining test that necessarily have to pass - though this will not ensure that you’ll have tests for everything, but at least the outer rim of your system is covered with test-cases.
When doing a lot of refactorings and perhaps things that go beyond refactoring, you need to add more tests - an architecture is a non-functional feature, you won’t get a source for defining tests for it, so you have to identify invariants and critical sections yourself and control them by test-cases. You will definitly start out with an incomplete list, now look what features you’ll test there:
- Things that went wrong in the past (Bug-reports translated as a test-case to fix it in a TDD.process or simply to ensure that the bugs does not get reintroduced)
- Things you understand well
- Replacements for apidocs
I postulate that this list is complete. I agree to split 2 into a) Trivialities and b) Specifications. The use of test-suites as a repository of example-code , well … we come to that later. Read the rest of this entry »
Posted in
Architecture, Java
No Comments »
April 18th, 2006
In episode 44 of the DrunkAndRetired PodCast the concept of one-way notebooks in Malaysia had been discussed and that in fact this applies also to servers in a certain sense: When the engineer hours for a problem become too expensive, it pays to replace the whole box instead of looking for the true cause.
This is not something new. Google for example (and also Amazon’s S3) runs on cheap, error-prone machines that are allow to fail just to be replaced by an other node. Obviously a cheap machine can be replaced cost-efficiently than an expensive box.
Rollback 20 years. Machines where expensive. Software had been tuned to exploit the machines to the leat CPU-cycle and if any thing was broken it had been fixed, because the ratio between engineering cost and hardware made this imperative.
For most apps today it does not pay to fine-tune the application, adding a bigger or an additional box is less expensive and more stable. As someone said some years ago: Software is called software because it is harder to change than hardware.
So far to the introduction, let’s think about the hard part: Software. Could we do something similar with software? Instead of fixing a bug, just dump it and take a new piece? Well, of course there is a difference between software and hardware: Just installing the software from another CD won’t fix your problem, while replacing a faulty motherboard by a new one will do.
Read the rest of this entry »
Posted in
Architecture, Java
1 Comment »
|