Виртуальная песочница (тм)

Tuesday, September 27, 2011

"Hall of API Shame: Boolean Trap" by ariya.ofilabs.com

The nice thing working for Trolltech was (among others) learning the principles behind the good API. The article Designing Qt-Style C++ API from Matthias 6 years ago is still a good reading till today. The content itself is now expanded into the wiki page API Design Principles.

The major premise behind a good API is rather straightforward:

the code is usually written once but read many times.

When writing the code, the developer has the time she needs to look at the API documentation and digest it. When someone else reads the code (for review or bug fix), she may not always have the API documentation handy. While this is just common sense, wait until you finish reading and see why this important fact is still overlooked these days.

Note: Qt is in C++, but surprisingly the same API design principles apply to the (wonderful) world of JavaScript, which is the focus in this blog post.

George Boole, inventor of the Boolean logic.

For this particular discussion, I’ll pick my favorite API design mistake: boolean trap. On this topic, the above API Design Principle wiki page says that

it’s almost invariably a mistake to add a bool parameter to an existing function

Let’s start with the textbook example: guess what this code means?

widget.repaint(false);

Without looking at the documentation, the usual suspect is don’t repaint the widget. Then, you look at the documentation and it refers the function argument as immediate, which is true if you want immediate painting or false for deferred painting. Thus, the correct behavior implied by the above code is actually repaint this widget later, which is miles away from your initial guess.

One possible solution to this problem is to use explicit function argument. In the C++ world, this can be solved using enum, e.g. widget.repaint(WidgetClass::Deferred). In the JavaScript world, the alternative is using an object literal such as,

widget.repaint({ immediate: false });

or the more verbose variant:

widget.repaint({ mode: "immediate" });

or just create a different function for that purpose:

widget.repaintLater();

There will be a concern of performance since an object is more expensive than just a simple boolean literal. Thus, profile your code carefully and set a sensible compromise if this above line is in your hot path. On the other hand, I also do believe that modern future JavaScript engines would be smart enough to optimize such usages that the speed penalty is negligible.

Another classic is the confusion during construction. Your user interface needs a bunch of sliders to allow the user to choose some values. Here is one line in the code for you to review:

var opacitySlider = new Slider(true);

Mysteriously, there are also lines similar to:

var volumeSlider = new Slider(false);

It turns out that true there means a horizontal slider and false means a vertical slider. Of course, the easiest way to clear this confusion is to actually name the object HorizontalSlider and VerticalSlider and get rid of the boolean argument. Heck, who knows someday you’ll need a diagonal slider!

You may scream, "Of course, I won’t be too idiot to make those rookie mistakes!". Well, in the following paragraphs I’ll give examples of actual boolean traps in the API of several well-known JavaScript libraries and frameworks out there (I try to be unbiased). Consider that there are millions of developers using the libraries in real-world web applications, imagine the exposure of the traps.

For each of this case, imagine you are doing a code review. Your amazing coworker wants to commit a patch and he consults you to check your opinion.

to be or not to be

This is the same as the textbook example, but coming from a real framework:

stackView.updateHeight(false);

Yes, that false again refers to immediate or not. To the untrained developer, the above line feels like don’t update the height. A real crazy one might even stretch it to update the width!

Here is another one. To facilitate easy iteration of child widgets, you can use next() function which would get you the next sibling. But then, the code looks like:

widget.next(true);

which actually does the extra magic (because of the true value) that the very first child widget will be returned if you hit the last one. In other words, true there stands for circular. An innocent value which does too much behind your back. Well, good luck trying to review that kind of code.

Another dangerous venture:

widget.destroy(false);

which potentially leads you to think don’t destroy this widget. You can’t be more wrong, the function actually still destroys your widget, but it leaves the DOM associated with the widget intact. Only if the argument is true then actually every related DOM pieces is also tore torn down.

optionally undecipherable

Now that we have the slider for the UI, we need to preset the value:

volumeSlider.setValue(90, false);

Another boolean horror! The documentation reveals that false there indicates that the slider should not animate the movement of its indicator from the old value to the new value. By default, it will show the animation but since we want to set the initial value, the animation will be distracting and needs to be off. How about writing it like this instead?

volumeSlider.setValue(90, { animation: false } );

There is this list view of all your customers. We want to find out who live in a certain city. Can you guess what the last optional argument refers to?

customerView.filter('address', 'sunnyvale', false);

Oh, apparently the API documentation refers it to caseSensitive! Just by looking at it, this is not obvious and it could mean an entirely different thing, anything from exactMatch to highlightMatchedLine. One possible workaround:

customerView.filter('address', 'sunnyvale', { caseSensitive: false });

the more, the merrier

While one boolean argument is already confusing, two boolean arguments can’t be more fun.

To handle layout, often there is a line of code that looks like:

cmp.setCentered(true, false);

Again, a trip to the API doc enlightens the reviewer that the function signature is actually setCentered(centered, autoUpdate). This is confusing as setCentered(centered) only is probably fine, it’s just like a property setter, but the interplay of the autoUpdate argument forces the brain to think harder.

Note that a pair of values like that, especially in the context of centering/geometry purpose, might provoke a different interpretation: center vertically and horizontally. This is arguably the most sensible one which comes to mind if one sees that code.

Here is another one:

menu.stop(true, false);

The boolean values there refer to clear the animation queue or not and go to the animation or not, respectively. They are not even remotely related. What is your best educated guess if you did not know this beforehand?

Of course, why stop at two if you can have more?

event.initKeyEvent("keypress", true, true, null, null,     false, false, false, false, 9, 0);

double negative

Now, coming back to property setter, this is one valid use of boolean argument, e.g. dialogBox.setVisible(true). However, care must be taken so that there is no such double negative. Especially for non-native speakers, double negative requires an extra careful measure to make sure that the right meaning is communicated.

If I wake you at midnight and ask you this question "if invisible is false, does that mean my component is shown or hidden?", there is a chance you answer it incorrectly.

Real-world examples of double negative follow:

volumeSlider.setThumbsDisabled(false); component.setDisabled(false); filter.setCaseInsensitive(false);

Would you be less confused if this is what you read instead?

volumeSlider.setThumbsEnabled(true); component.setEnabled(true); filter.setCaseSensitive(true);

The same principle applies to active vs inactive, modified vs unmodified, defined vs undefined, selected vs unselected, etc.

By now, hopefully you got the idea of various risky uses of boolean argument. Feel free to share your favorite freak-out moment as you encounter such a similar trap.

Most importantly, next time you design an API function, remember George Boole and don’t let him down!

Update: Some people on Reddit pointed out that they would not interpret widget.repaint(false) as do not repaint. First of all, it’s subjective. In some languages it can be understood as repaint not, which is effectively a negation. Also, the context might pollute, e.g. if there is fooWidget.show(false) (which means do not show) right before, then it may influence a similar conclusion for the repaint issue. I was also not clear that any crazy possible interpretations are just examples, substitute them with your own imaginations. The fact that everyone can propose a different interpretation is the premise: ambiguity begets insanity.

[source]

Read more...

User Interface Engineering: "Do users change their settings?"

Jared Spool

September 14th, 2011

Back in the early days of PC computing, we were interested in how people used all those options, controls, and settings that software designers put into their applications. How much do users customize their applications?

We embarked on a little experiment. We asked a ton of people to send us their settings file for Microsoft Word. At the time, MS Word stored all the settings in a file named something like config.ini, so we asked people to locate that file on their hard disk and email it to us. Several hundred folks did just that.

We then wrote a program to analyze the files, counting up how many people had changed the 150+ settings in the applications and which settings they had changed.

What we found was really interesting. Less than 5% of the users we surveyed had changed any settings at all. More than 95% had kept the settings in the exact configuration that the program installed in.

This was particularly curious because some of the program’s defaults were notable. For example, the program had a feature that would automatically save your work as edited a document, to prevent losing anything in case of a system or program failure. In the default settings for the version we analyzed, this feature was disabled. Users had to explicitly turn it on to make it work.

Of course, this mean that 95% of the users were running with autosave turned off. When we interviewed a sample of them, they all told us the same thing: They assumed Microsoft had delivered it turned off for a reason, therefore who were they to set it otherwise. “Microsoft must know what they are doing,” several of the participants told us.

We thought about that and wondered what the rationale was for keeping such an important feature turned off. We thought that maybe they were concerned about people running off floppies or those who had slow or small disks. Autosave does have performance implications, so maybe they were optimizing the behavior for the worst case, assuming that users who had the luxury to use the feature would turn it on.

We had friends in the Microsoft Office group, so we asked them about the choice of delivering the feature disabled. We explained our hypothesis about optimizing for performance. They asked around and told us our hypothesis was incorrect.

It turns out the reason the feature was disabled in that release was not because they had thought about the user’s needs. Instead, it was because a programmer had made a decision to initialize the config.ini file with all zeroes. Making a file filled with zeroes is a quick little program, so that’s what he wrote, assuming that, at some point later, someone would tell him what the “real defaults” should be. Nobody ever got around to telling him.

Since zero in binary means off, the autosave setting, along with a lot of other settings, were automatically disabled. The users’ assumption that Microsoft had given this careful consideration turned out not to be the case.

We also asked our participants for background information, like age and occupation, to see if that made a difference. It didn’t, except one category of people who almost always changed their settings: programmers and designers. They often had changed more than 40% (and some had changed as much as 80%) of the options in the program.

It seems programmers and designers like to customize their environment. Who would’ve guessed? Could that be why they chose their profession?

(Big takeaway: If you’re a programmer or designer, then you’re not like most people. Just because you change your settings in apps you use doesn’t mean that your users will, unless they are also programmers and designers.)

We’ve repeated this experiment in various forms over the years. We’ve found it to be consistently true: users rarely change their settings.

If your application has settings, have you looked to see what your users do? How many have changed them? Are the defaults the optimal choice? Does your settings screen explain the implications of each setting and give your users a good reason for mucking with the defaults?

Read more...

Saturday, September 24, 2011

Viking Laws


1. BE BRAVE AND AGGRESSIVE
BE DIRECT
GRAP ALL OPPORTUNITIES
USE VARYING METHODS OF ATTACK
BE VERSATILE AND AGILE
ATTACK ONE TARGET AT A TIME
DON'T PLAN EVERYTHING IN DETAIL
USE TOP QUALITY WEAPONS

2. BE PREPARED
KEEP WEAPONS IN GOOD CONDITIONS
KEEP IN SHAPE
FIND GOOD BATTLE COMRADES
AGREE ON IMPORTANT POINTS
CHOOSE ONE CHIEF

3. BE A GOOD MERCHANT
FIND OUT WHAT THE MARKET NEEDS
DON'T PROMISE WHAT YOU CAN'T KEEP
DON'T DEMAND OVERPAYMENT
ARRANGE THINGS SO THAT YOU CAN RETURN

4. KEEP THE CAMP IN ORDER
KEEP THINGS TIDY AND ORGANIZED
ARRANGE ENJOYABLE ACTIVITIES WHICH STRENGTHEN THE GROUP
MAKE SURE EVERYBODY DOES USEFUL WORK
CONSULT ALL MEMBERS OF THE GROUP FOR ADVICE


Read more...

Бутерброды со шкварками и луком по-крестьянски

Продукты: хлеб, репчатый лук, шпик.

Шпик нарезать мелкими кусочками, вытопить. В полученном жире со шкварками обжарить лук, массу охладить, намазать на ломтики хлеба. Подавать к горячим кислым щам. Read more...

Суп с картофельными ушками

Куриный или мясной бульон, зелень укропа или петрушки, 4-5 картофелин среднего размера, 1 яйцо, 2-3 ст. ложки муки.

Картофель отварить в мундире, очистить, истолочь или порпустить через мясорубку, добавить сырое яйцо, соль, муку, замесить картофельную массу. Разделать массу на небольшие шарики, из которых скалкой раскатать кружки (как для пельменей). Слепить из кружков ушки, опустить их в кипящий бульон и отварить.

Суп подавать с рубленой зеленью укропа или петрушки. Read more...

Квас сухарный

Ржаные сухари обжаривают в духовке так, чтобы они зарумянились, но не подгорели, складывают в бочонок, бачок или кастрюлю, заливают теплой водой (70-80 градусов по Цельсию) из расчета 1.8л на 1кг сухарей и оставляют в теплом месте на 2ч, переодически помешивая. Настой сливают, а оставшиеся сухари заливают еще раз водой (1-1.2л на 1кг), настаивают 1-2ч и сливают настой в полученный ранее. Затем полученное сусло охлаждают до 20-25 градусов по Цельсию, добавляют сахар, дрожжи, разбавленные этим же суслом, настой мяты (можно готовить квас и без мяты) и оставляют в тепле на 8-12ч. Разлив в бутылки, хранят на холоде.

На 1кг сухарей:
* сахар - 1.5кг,
* дрожжи - 40г,
* вода - 3л,
* мята. Read more...

Sunday, September 4, 2011

RSA Animate Matthew Taylor: Left brain, right brain

Read more...

RSA Animate - Choice

Read more...

RSA Animate - The Internet in Society: Empowering or Censoring Citizens?

Read more...

RSA Animate - 21st Century Enlightenment

Read more...

RSA Animate - First as Tragedy, Then as Farce


Read more...

Saturday, September 3, 2011

RSA Animate - Superfreakonomics

Read more...

RSA Animate - The Empathic Civilisation

Местами наивно-утопично, но все равно интересно. И рисунки... В общем - для коллекции. :)



Read more...

RSA Animate - The Secret Powers of Time

The original presentation:



With Spanish subtitles:

Read more...

RSA Animate - Language as a Window into Human Nature

Read more...

RSA Animate - Changing Education Paradigm

Read more...

RSA Animate - Crises of Capitalism


The original presentation:




The critique:

Read more...

RSA Animate - Smile or Die



Чрезмерная и повсеместная увлеченность позитивным мышлением - одна из причин недавнего финансового кризиса. Read more...

Thursday, September 1, 2011

JUG.LV - Meeting #10 on 8-Sep-2011 - registration started!

Agenda:


Java User Group Meeting

When: September 8th, 2011
Where: Tallink Hotel Riga, Elizabetes 24, Riga, Latvia

18:15 : Welcome {
18:30 - 18:40 - Java User Group Updates
18:40 - 18:55 - C.T.Co - Sponsored Talk
18:55 - 19:55 - Kirill Menshikov - GemFire: In-Memory Data Grid
19:55 - 20:10 - coffee break
20:10 - 21:00 - Kirill Afanasjev - Google Guicehttp://www.blogger.com/img/blank.gif
21:00 - 21:10 - Wrap-up and raffle
21:10 : }

70 tickets remaining at the moment of writing this.
Read more...

RSA Animate - The Truth about Motivation

Read more...