Requirements Gathering: set up to fail

Image by Icons Land - http://www.icons-land.com

Without project requirements gathering, a project is nothing. One failure point is letting the wrong people gather the requirements.

Image courtesy Rebecca Dominguez (CC BY-NC-SA 2.0)

There are basically two types of requirements for an application project: the functional/feature-set and the technical.

Traffic ConePitfall: There must be at least one cycle of comparing Functional to Technical requirements to ensure they sync up, followed by adjustments to both (as necessary).

Functional

This answers the two questions:

  1. What will this application do?
  2. How will the user interact with the application to get #1?
function requirements gathering can specify oven-baked fries

Oven baked fries (source: Wikipedia)

If you want your application to feed the user by ordering from Fries-2-Go™, the 24-hour french-fry delivery service (fries in 27.5 minutes or you supersize for free!), that is #1. If  you say that the user will push the Big Red Button on the app, then say what flavor fries they want (Creamy Chicken Velouté, Herb Hollandaise, or Buttery Béchamel), that is #2.

Note that we didn’t say HOW this magic happens. That is not the purview of the Functional Requirements.

Traffic ConePitfall 1: the stakeholders (especially people who are representative of those who will use the app) MUST participate when crafting the requirements, especially any workflow.

Traffic ConePitfall 2: failing to involve an experienced technical architect during this phase may result in defining requirements that are not technically feasible, craft a clumsy/unwieldy workflow, or miss borrowing from solutions in similar applications.

Technical

These requirements are concerned with the plumbing, the hidden part of the iceberg and the underground kingdom of the Troll People. Functional requirements—from a high level—are absolutely required (see what I did?) to be defined before the technical requirements are attempted.

Traffic ConePitfall: any attempt by non-technical folks to attempt to work on these will result in flawed implementation, busted schedule, cost overruns and lowered team morale (lowered productivity).

Failure Examples

A high-level manager defined functional requirements with no input from field staff or technical architects. She then defined technical requirements based upon an internal standards white-paper, but without the understanding necessary to apply the standards to this project.

The technical staff was brought in at the last minute and told to review the requirements quickly so that work could begin. Immediately, the staff noticed several major flaws. For example, one of the functional requirements violated app store constraints, which would prevent the app from ever being accepted by the app store. In addition, the requirement was completely unnecessary, as there were external apps that provided the same features.

The manager (perhaps to save face) ordered the project proceed with the original functional requirements intact. This resulted in a product that cost more than necessary, it could not be distributed via well-known app stores, and it contained useless and confusing functionality.

Enhanced by Zemanta

Installing Internet Explorer on Mac

Credit: Hopstarter

Edit (2014-07-11): Fixed URLs

When you need to develop/design a solution for the majority of corporate users, you will need to test it on Internet Explorer. If you have a Mac, setting this up on your machine is easy.

The original source for this information was OSXDaily. I cleaned it up and added additional information.

Intended Audience

TerminalIf you’re unfamiliar with using the terminal, these instructions will not help you. The point is to allow you to install Internet Explorer on Mac for the purposes of testing and developing web applications and sites. Ideally, you are one of the following:

  • Web Developer
  • Web Designer
  • QA Tester

If you plan on running Internet Explorer for other purposes (such as working with an IE-only site), then this is probably not the best solution for your needs.

Required software

  1. Oracle VirtualBox
  2. curl (from Mac Ports or other)

Procedure

Be aware, this process can take HOURS to do, may crash in the middle and cause you to start over, take up inordinate amounts of disk space, etc.

Install IE7 Only

curl -s https://raw.githubusercontent.com/xdissent/ievms/master/ievms.sh | env IEVMS_VERSIONS="7" bash

Install IE8 Only

curl -s https://raw.githubusercontent.com/xdissent/ievms/master/ievms.sh | env IEVMS_VERSIONS="8" bash

Install IE9 Only

curl -s https://raw.githubusercontent.com/xdissent/ievms/master/ievms.sh | env IEVMS_VERSIONS="9" bash

Install IE7, 8 and 9

curl -s https://raw.githubusercontent.com/xdissent/ievms/master/ievms.sh | bash

Notes

Once you have the virtual machines installed, fire them up, set up the Windows instance (install drivers, etc.), then take a snapshot. This is the one you will always use.

When you get a ‘you must activate’ notice, open a Windows cmd line and run

slmgr –rearm

You can rearm two times before it won’t work anymore. At that point, roll back to your snapshot and you can rearm again when you get the message. Obviously, when you roll back to your snapshot all changes will be discarded (that’s the point), so make sure you save any data on your host’s drive.

FAQ

Q. Where is the command line on my Mac?
A. It is not recommended that you use these instructions; instead try another solution such as Apple Boot Camp.

Q. How do I install/uninstall Oracle Virtual Box?
A. You can try looking for information on the Oracle Virtual Box website or contact the Genius Bar at your local Apple Store for assistance.

Q. Where are the windows snapshots stored?
A. In ~/.ievms/

Q. The download stalls or crashes.
A. If it stalls, check your internet connection; you may have to restart the install. In the event of a crash, examine the error message to determine the cause of the problem.

Q. Can you just install it for me?
A. Sorry, no.

Don’t hate your users

If you want to enable your users to do something, such as create an account on your system, DO NOT MAKE IT IMPOSSIBLY HARD.

Case in point:

This image will make you cry

Go ahead and register, I dare you

If algebra is too hard, just refresh and you’ll see something else.

Another scary image

I laugh at your feeble Calculus skills!

Woah, better refresh.

You can't escape the Maths

Make your time

You know what this does? It not only keeps out any bots, but turns a normal human into something else:

rageface

Apoplectic: overcome with anger; extremely indignant.

With a zillion other websites out there, are you sure yours is compelling enough or contains such rare information that people will jump the gorge to get to it?

https://www.youtube-nocookie.com/embed/P7vte1epVpE?rel=0

 

How to ask for help the wrong way

When submitting  bug reports, it is a good idea to

A room full of computers all showing the BSOD

  1. Realize that you’re asking for help from people who (usually) have day jobs, and
  2. Expend at least some amount of effort to show you’re not expecting someone else to do all the work.

With that in mind, let me introduce to you the Best Bug Report Comment, Ever

First the bug report:

[snip]

I don’t have the exact errors to post because I deleted my compile log, but they are the same errors you get if you don’t have the bzip2 development libraries installed, which of course I do in /www

[snip]

Then someone helpful asks for more information.

Please recompile so that you can tell us te exact errors.

Derick

And then, GOLD:

The php developer who added/maintains bzip2 support will know what I am talking about. I am not going to compile when I know this! It would be a waste of my time.


Wow.

Now, not to worry; a few minutes later the submitter saw the error of his ways, compiled his code, posted the exact error message and got help.

Learning how to ask questions is a skill. Mastering this skill can only help, because everyone (even the Super-cool techno guru) has to ask for help at some point, so why not be as effective as possible?

Until I find another one. That place is GOLD!

Enhanced by Zemanta

Decoupling presentation from content

Box model in CSS

Box model in CSS (credit: Wikipedia)

I recently ran across the anti-pattern of what I see as a common problem amongst designers and developers: coupled presentation and content. I’ve found that decoupling the presentation from the content makes things much easier to write, maintain and expand.

Here’s a simple example:

HTML

<section>
    <div class="margin-top-10">Lorem Ipsum</div>
</section>

CSS

.margin-top-10 { margin-top: 10px };
.margin-top-20 { margin-top: 20px };
(etc)

Take a look at what is going on here: we’re adding a 10px margin to the top of the div. DON’T DO THIS. You want your class names to be contextual, not descriptive of the style.


Rule of thumb

To change the layout, you should only have to edit the CSS, not the HTML.

Here’s where our anti-pattern falls down and will cause grief.

  1. You decide to adjust the positioning of the section. You can:
    • Edit the CSS, changing the class’s margin value and breaking every other element that uses that class.
    • Edit the HTML create a new class, then edit its CSS class definition. If you have to experiment with different margin values, you’ll need a LOT of classes. “Will 14px work or 15px? What about .25em? Argh!”
  2. You can’t have too many attributes in each class, because they will have unintended consequences for the other elements that are using them. Add a red border to one class because you need a border for a specific element, now you have red borders on ALL the elements that share that class. So, you’ll have to have many single- (or few-) attribute values, and include all of the necessary ones on the required HTML elements.
  3. The violent psychopath maintenance programmer (who knows where you live) will kill you in your sleep. You have made her job insanely hard by turning this:
<div class="margin5 blueborder mediumwidth floatingleft" ...

into this

<div style="margin:5px;border:3px blue outset;float:left;width:75%" ...

for no good reason.

The Cure

Think about the element in terms of content or a functional space. What is it and what does it do? In our example above, let’s assume it is the lede section of an article. Then we would do:

HTML

<section>
    <div class="lede">Lorem Ipsum</div>
</section>

CSS

.lede { 
  margin-top: 10px;
  border-bottom: 2px #9fe2f9 outset;
  float: right;
  position: relative;
  width: ...
};

By decoupling the content (div) from the presentation (style-dependent class), we are free  to adjust the style of that element by making whatever changes to the CSS and leaving the HTML alone.

“But,” you shriek, “I have common elements for everything! Rounded corners! Gradients! (except IE…) Et cetera!”

 

For this, we will turn to our trusty companions Less and/or Sass in a future post.

SASS: Style w/ Attitude

SASS: Style w/ Attitude

Enhanced by Zemanta

Iterations in Less

Part of the beauty of Less and other CSS ‘compilers’ is to enable the author to automate tedious functions that normally must be coded by hand.

Cut Copy Paste

Cut Copy Paste (Photo credit: arthit)

Suppose you needed several classes that specified padding/margins:

.mRight50{margin-right:50px}
.mLeft50{margin-left:50px}
.pRight50{padding-right:50px}
.pLeft50{padding-left:50px}
.mRight25{margin-right:25px}
.mLeft25{margin-left:25px}
.pRight25{padding-right:25px}
.pLeft25{padding-left:25px}

No big deal, right? It wouldn’t take that long to type in; just cut and paste a bit.

Well, what if you needed them from 0-100 by 5s? (Never mind WHY you’d want to do this; this is a simple example.)

.mRight100{margin-right:100px}
.mLeft100{margin-left:100px}
.pRight100{padding-right:100px}
.pLeft100{padding-left:100px}
.mRight95{margin-right:95px}
.mLeft95{margin-left:95px}
.pRight95{padding-right:95px}
.pLeft95{padding-left:95px}
.mRight90{margin-right:90px}
.mLeft90{margin-left:90px}
.pRight90{padding-right:90px}
.pLeft90{padding-left:90px}
.mRight85{margin-right:85px}
.mLeft85{margin-left:85px}
.pRight85{padding-right:85px}
.pLeft85{padding-left:85px}
.mRight80{margin-right:80px}
.mLeft80{margin-left:80px}
.pRight80{padding-right:80px}
.pLeft80{padding-left:80px}
.mRight75{margin-right:75px}
.mLeft75{margin-left:75px}
.pRight75{padding-right:75px}
.pLeft75{padding-left:75px}
.mRight70{margin-right:70px}
.mLeft70{margin-left:70px}
.pRight70{padding-right:70px}
.pLeft70{padding-left:70px}
.mRight65{margin-right:65px}
.mLeft65{margin-left:65px}
.pRight65{padding-right:65px}
.pLeft65{padding-left:65px}
.mRight60{margin-right:60px}
.mLeft60{margin-left:60px}
.pRight60{padding-right:60px}
.pLeft60{padding-left:60px}
.mRight55{margin-right:55px}
.mLeft55{margin-left:55px}
.pRight55{padding-right:55px}
.pLeft55{padding-left:55px}
...

Ugh.

There’s a better way:


@steps: 100;

// Main Loop
.sidesX( @index ) when ( @index > 0 ) {
 (~".mRight@{index}") { .mRightX(@index); }
 (~".mLeft@{index}") { .mLeftX(@index); }
 (~".pRight@{index}") { .pRightX(@index); }
 (~".pLeft@{index}") { .pLeftX(@index); }

 .sidesX(@index - 5);
}

// End iteration at index zero
.sidesX( 0 ) {}<

// Individual class rendering
.mRightX( @offsetsize ) {
  margin-right: (~"@{offsetsize}px");
}
.mLeftX( @offsetsize ) {
 margin-left: (~"@{offsetsize}px");
}
.pRightX( @offsetsize ) {
 padding-right: (~"@{offsetsize}px");
}
.pLeftX( @offsetsize ) {
 padding-left: (~"@{offsetsize}px");
}

// Generate the CSS
.sidesX( @steps );
Enhanced by Zemanta

Flashback: Skip Intro for flash introductions

Remember when flash introduction pages were all the rage? They were ‘cool’ from the web designer‘s standpoint, but utterly annoying and off-putting to the visitor. Fortunately, most people figured out that people visited their site for the content, not the snappy graphics (unless it was a gallery site), and certainly not for the mandatory intro pages.

Yet, some people still haven’t gotten the clue that the 80s called and they want their flash intros back.

xkcd: the seventies called

For those who remember with revulsion, here’s the old SkipIntro parody. The site is long gone, but it would be a shame to let it fade away!

SkipIntro

(click the ‘play ball’ to start)

Get Adobe Flash player

If you haven’t clicked on it, do it now! Relive the pain of the never-ending flash intro to the sound of weird indian music and gunfire!

Enhanced by Zemanta

Regular Expressions Roundup

Writing some Regular Expressions?

Some people, when confronted with a problem, think “I know, I’ll use regular expressions.” Now they have two problems.
—Jamie Zawinski

Well, not really. There are some cases where using a Regular Expression—RegEx— instead of a heap of convoluted if statements just makes sense from both a lazy and practical standpoint.

When you’re knee-deep in writing your RegEx, you’ll need to test. My favorite RegEx ‘workbench’ is Oliver Steele’s “reWork.”

screenshot of reWork

To get a jumpstart on writing complex RegEx,  check out the Regular Expression Library, which contains a plethora of user-submitted RegEx recipes. Some of them are quite good (check each recipe’s rating).

screenshot of example page

Enhanced by Zemanta

Find Something You Like and Dissect It

Image representing Wikipedia as depicted in Cr...

Image via CrunchBase

I’m always on the lookout for a new technique or Better Mousetrap. I admit I don’t know all that much, so I’m happy to learn.

I was playing around with Wikify @ appointment.net (a nifty tool that goes through a block of text and ‘wikifies’ it–that is, links all the words it can find to relavant Wikipedia articles) when I noticed the behavior seemed rather…odd. I could see it go through the word list as it created links, and every time it linked up a word, every duplicate word was linked.

Let’s take some example text (from the now-defunct Dilbert Mission Statement Generator) and run it through the site:

“We have committed to synergistically fashion high-quality products so that we may collaboratively provide access to inexpensive leadership skills in order to solve business problems

Our mission is to continually leverage existing seven-habits-conforming catalysts for change as well as to competently leverage other’s error-free materials.

We globally leverage other’s professional meta-services as well as to conveniently integrate competitive solutions in order to solve business problems.

“It is our job to continually foster world-class infrastructures as well as to quickly create principle-centered sources to meet our customers needs

“Our challenge is to assertively network economically sound methods of empowerment so that we may continually negotiate performance based infrastructures

For example, the additional instances of “leverage,” “problems,” and “business” were quickly linked, once the first one was completed. Poking around their code, I noticed all the action takes place in wikify.js. There are a few gems in there. For example, the function call to reduce an array to only unique values:

function array_unique( array ) {
    // http://kevin.vanzonneveld.net
    // +   original by: Carlos R. L. Rodrigues (http://www.jsfromhell.com)
    // +      input by: duncan
    // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   bugfixed by: Nate
    // +      input by: Brett Zamir (http://brettz9.blogspot.com)
    // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   improved by: Michael Grier
  // %          note 1: the second argument, sort_flags is not implemented
    // *     example 1: array_unique(['Kevin','Kevin','van','Zonneveld','Kevin']);
    // *     returns 1: ['Kevin','van','Zonneveld']
    // *     example 2: array_unique({'a': 'green', 0: 'red', 'b': 'green', 1: 'blue', 2: 'red'});
    // *     returns 2: {'a': 'green', 0: 'red', 1: 'blue'}

    var key = '', tmp_arr1 = {}, tmp_arr2 = [];
    var val = '';
    tmp_arr1 = array;

    var __array_search = function (needle, haystack) {
        var fkey = '';
        for (fkey in haystack) {
            if ((haystack[fkey] + '') === (needle + '')) {
                return fkey;
            }
        }
        return false;
    };

    for (key in tmp_arr1) {
        val = tmp_arr1[key];
        if (false === __array_search(val, tmp_arr2)) {
            tmp_arr2[key] = val;
        }
        delete tmp_arr1[key];
    }
    return tmp_arr2;
}

Aha! See how that works?

Enhanced by Zemanta

1 2 3