Lean UX is a collection of practices for approaching the design of products and interfaces that draws from lean manufacturing, agile development, and the lean startup movement in its focus on core business-focused principles.
This article explains the core points of the movement and distills it down to 6 points in manifesto format. I reads like the agile manifesto, talking about preferences, and I think these documents serve nicely as guides rather than checklist.
I wanted something I could post up on my wall next to Nielsen's 10 Usability Heuristics , so I took the manifesto text and turned it into something that I can pin up to my wall. Hopefully it is useful to someone else.
Lean UX Manifesto, PDF
While I was at it, I did the same thing to the Agile Manifesto.
Agile Manifesto, PDF
EDIT: 4/26/2016
I couldn't find the PDF version of Nielsen's Usability Heuristics, so I re-created that as a PDF as well.
Nielsen's 10 Usability Heuristics, PDF
Monday, April 21, 2014
Wednesday, April 9, 2014
Exposing global libraries as module constants in angularjs
I want to use the awesome underscore library with angular. Although you can just drop the library in your html file and reference the _ variable as a global, I wanted to pass it into my app's module with angular's dependency injection. The main advantages here are:
1. Explicit definition of dependencies
Having the objects/services/controller/etc that something depends on defined makes it easier to re-use components elsewhere. This helps avoid the annoying '_' is not defined type of errors, but also helps prevent some more subtle bugs if you are depending on a undefined global in a part of your code that doesn't get run very often.
2. Testability
If we inject underscore, we could mock it out or wrap it during a test. This would allow us to do cool things like see how many times a certain underscore function was called.
How do you do this? At first I read this article that discussed wrapping it in a factory. This certainly works, but since angular has the nice interface for defining inject-able constants, I used that instead. So all the code you need is:
var app = angular.module('MyApp');
app.constant('_', window._ );
Yep. That's it. And the first line is just to give you context.
To use this in a controller, do something like this:
app.service('MyService', ['_', function(_){
// Your code goes here
}])
Pretty simple.
1. Explicit definition of dependencies
Having the objects/services/controller/etc that something depends on defined makes it easier to re-use components elsewhere. This helps avoid the annoying '_' is not defined type of errors, but also helps prevent some more subtle bugs if you are depending on a undefined global in a part of your code that doesn't get run very often.
2. Testability
If we inject underscore, we could mock it out or wrap it during a test. This would allow us to do cool things like see how many times a certain underscore function was called.
How do you do this? At first I read this article that discussed wrapping it in a factory. This certainly works, but since angular has the nice interface for defining inject-able constants, I used that instead. So all the code you need is:
var app = angular.module('MyApp');
app.constant('_', window._ );
Yep. That's it. And the first line is just to give you context.
To use this in a controller, do something like this:
app.service('MyService', ['_', function(_){
// Your code goes here
}])
Pretty simple.
Saturday, April 5, 2014
Fixing missing VCBuild.exe
From the yeoman generator for angular, when installing socket.io:
MSBUILD : error MSB3428: Could not load the Visual C++ component "VCBuild.exe". To fix this, 1) ins
tall the .NET Framework 2.0 SDK, 2) install Microsoft Visual Studio 2005 or 3) add the location of
the component to the system path if it is installed elsewhere.
These instructions may be useful when encountering less helpful messages about a missing "VCBuild.exe" file in other programs.
You can download the .NET framework v2.0 SDK here.
You can download the Visual C++ 2005 ISO from the link in this blog post.
Then you can use this utility from Microsoft to mount the iso image and run the installer. To mount the ISO, follow the instructions in the README extracted from the utility. Make sure to run the Virtual CD ROM Control Panel executable as administrator.
I had problems with permissions with that utility, so I took the easy way out and just burned the Visual Studio ISO to a disk. VirtualCloneDrive probably would have worked but I didn't feel like messing with it. After all, this is just a step toward configuring a development environment...
I received a few errors about compatibility issues when installing for both Visual Studio 2005 and MSSQL Server Express. I ignored those and continued with the installation. The files of interest were placed in C:\Program Files (x86)\Microsoft Visual Studio 8\VC. I also noticed vcvarsall.bat (a file that gets referenced many times when trying to compile components on Windows) was just one directory up at C:\Program Files (x86)\Microsoft Visual Studio 8\VC. I added both to my PATH.
After those steps, everything worked fine.
EDIT
I ran into this same problem on another machine, and found this solution for installing the 2008 Express Edition of Visual Studio. Follow the link and run the installer. This worked as well as installing the 2005 edition but was much faster.
Note that this second solution just installs a 32 bit compiler. Also it may be necessary to install a different version of the compiler depending on the version of python you are running.
MSBUILD : error MSB3428: Could not load the Visual C++ component "VCBuild.exe". To fix this, 1) ins
tall the .NET Framework 2.0 SDK, 2) install Microsoft Visual Studio 2005 or 3) add the location of
the component to the system path if it is installed elsewhere.
These instructions may be useful when encountering less helpful messages about a missing "VCBuild.exe" file in other programs.
You can download the .NET framework v2.0 SDK here.
You can download the Visual C++ 2005 ISO from the link in this blog post.
I had problems with permissions with that utility, so I took the easy way out and just burned the Visual Studio ISO to a disk. VirtualCloneDrive probably would have worked but I didn't feel like messing with it. After all, this is just a step toward configuring a development environment...
I received a few errors about compatibility issues when installing for both Visual Studio 2005 and MSSQL Server Express. I ignored those and continued with the installation. The files of interest were placed in C:\Program Files (x86)\Microsoft Visual Studio 8\VC. I also noticed vcvarsall.bat (a file that gets referenced many times when trying to compile components on Windows) was just one directory up at C:\Program Files (x86)\Microsoft Visual Studio 8\VC. I added both to my PATH.
After those steps, everything worked fine.
EDIT
I ran into this same problem on another machine, and found this solution for installing the 2008 Express Edition of Visual Studio. Follow the link and run the installer. This worked as well as installing the 2005 edition but was much faster.
Note that this second solution just installs a 32 bit compiler. Also it may be necessary to install a different version of the compiler depending on the version of python you are running.
Wednesday, April 2, 2014
Backbone View Event Types
Today I read through Derick Bailey's excellent post on memory management in Backbone view code. I was a bit confused by all the different ways to register and un-register events and what they were all for. Specifically I was confused by his close() method, used to clean up view events.
Why was unbind() needed in addition to remove()? Why was stopListening() not needed?
So I put together a simple guide for myself.
After putting this together the code makes more sense.
You do need to call unbind() in addition to remove() because remove() handles DOM events and unbind() handles Backbone events.
You don't need to call stopListening() because unbind() catches everything stopListening() would catch. You don't need undelegateEvents() because jquery's remove() removes all of the events on that DOM element for you.
Hope that helps.
Why was unbind() needed in addition to remove()? Why was stopListening() not needed?
So I put together a simple guide for myself.
- undelegateEvents() calls off() for all events registered through backbone's delegateEvents()
- usually this means all events named in the 'events' object passed to a view, which hande DOM events
- code: http://backbonejs.org/docs/backbone.html#section-139
- delegateEvents() works differently from directly binding events with jquery's on()
- code: http://backbonejs.org/docs/backbone.html#section-138
- namespaces events under .delegateEvents{view cid}
- this makes it easy to remove events only for this view with undelegateEvents()
- stopListening() calls off() for all events registered through backbone, on this._listeningTo
- usually these are non-DOM events
- code: http://backbonejs.org/docs/backbone.html#section-23
- unbind() is the same as off() now
- can captures anything that doesn't go through listenTo() (and everything that does)
- code: http://backbonejs.org/docs/backbone.html#section-30
- docs: http://backbonejs.org/#Events-listenTo
- the main advantage of using listenTo() is easy cleanup
- remove() delegates to jquery's remove()
- docs: http://api.jquery.com/remove/
- removes DOM elements, bound events, and data
After putting this together the code makes more sense.
You do need to call unbind() in addition to remove() because remove() handles DOM events and unbind() handles Backbone events.
You don't need to call stopListening() because unbind() catches everything stopListening() would catch. You don't need undelegateEvents() because jquery's remove() removes all of the events on that DOM element for you.
Hope that helps.
Sunday, January 19, 2014
Steps I took for moving my Wordpress site to be hosted on Earthlink
My neighborhood's home owner's association is using Earthlink hosting. Most of the cheap shared hosting services have some eccentricities that you may run into when trying to do anything outside the standard "1 click installer" type of actions. Earthlink seems to be especially behind the times.
Some clues:
- Their Wordpress installer provides a very outdated version of Wordpress.
- They install Wordpress into a directory you do not have ssh or ftp access to, which makes automatic updates impossible.
- During my fiddling with user database accounts, I managed to get the web panel into a state where I was locked from doing anything to my phpmyadmin installation.
Getting to the point: here's what I did to get the latest version of Wordpress (with updated plugins and a database full of pages) migrated from a server I was running locally.
1. Use the Earthlink wordpress installer
This creates the wp-config.php and puts it into /private. It also creates the database, which is not a big deal but it's nice. It also probably fixes up htaccess and some other settings, which you can't edit.
You can't stop here though because you can't update your Wordpress installation, and running with an old version of Wordpress is bad for security and breaks many of the plugins that make Wordpress awesome.
2. Clean up your database dump file
You used mysqldump to get the data from your old database, right? That's good, but you need to change the urls in the database dump file. I was able to do this quickly using find/replace on the dump sql file.
Also, because the commands in the dump files include
"lock" and "unlock" statements and Earthlink doesn't give you those
permissions on your database users for some reason, you need to remove
all of these commands. I ended up just running the commands for each
table one at a time, removing the "lock" and "unlock" statements for
each table before executing.
3. Install phpmyadmin
For some reason I couldn't run any sql commands from the command line. So In installed phpmyadmin, which worked fine.
One thing to look out for is that you have to associate the phpmyadmin installation with a single database when you add it. If you delete the user associated with the database through Earthlink's console, the database is also deleted, and this breaks phpmyadmin to a point where you can't re-install, uninstall, or re-associate the phpmyadmin installation with a different database. At that point, you have to talk to support.
One thing to look out for is that you have to associate the phpmyadmin installation with a single database when you add it. If you delete the user associated with the database through Earthlink's console, the database is also deleted, and this breaks phpmyadmin to a point where you can't re-install, uninstall, or re-associate the phpmyadmin installation with a different database. At that point, you have to talk to support.
4. Delete all the crap the Wordpress install placed in the /public folder
Yes, you just added it, but this is where your new Wordpress files will go. Make sure to not delete the /phpmyadmin symlink in that directory. You need that.
5. Copy all of files for your local Wordpress install into Earthlink's /public folder
Here "local Wordpress install" means all the files from the installation you already had running somewhere else. FTP works fine here.
6. Copy the wp-config.php files from /private to /public
This file is pretty important. You could fiddle around with the wp-load file to get it to fine the wp-config file in /private, but I found it simpler just to move the file.
That should be it! It felt a little unclean, but it got the job done and I'm now running a modern copy of Wordpress that I can easily update because all of the files are in a folder I have FTP access to.
Wednesday, August 7, 2013
Variable scoping in python and javascript
As I was reviewing a colleague's code a few days ago, I ran across the following:
Having spent the last few weeks reading through Code Complete, defining the variables outside the loop stuck out to me as a problem - a case of pre-mature optimization. I suggested the following:
Reducing the scope of the variable just felt cleaner, and the discussions I found here and here seemed to back this up.
But when this came up when talking with a friend later in the day, he mentioned that he didn't think javascript even maintained scope inside "for" loops. Coming from C++, I thought loops in both javascript and python would hold scope. The following were our test functions, entered on the Chrome Developer Tools command line for javascript and the REPL for Python.
The output from both these tests are the same: x is defined. This is because both python and javascript lack block scope, something very much present in C++.
In the particular case I was discussing, the choice becomes a big more grey. There are strong opinions on both sides of the fence. At this point I agree with the accepted answer on the linked StackOverflow discussion: "For the case where a variable is used temporarily in a section of code, it's better to declare var in that section, so the section stands alone and can be copy-pasted." I think the use "var" can be as helpful for people as compilers, and it makes the intent of a block of code more clear. Using "var" on a variable in a code block says to me that this variable is intended for use in this code block, and may not have any meaning outside of it.
Having spent the last few weeks reading through Code Complete, defining the variables outside the loop stuck out to me as a problem - a case of pre-mature optimization. I suggested the following:
Reducing the scope of the variable just felt cleaner, and the discussions I found here and here seemed to back this up.
But when this came up when talking with a friend later in the day, he mentioned that he didn't think javascript even maintained scope inside "for" loops. Coming from C++, I thought loops in both javascript and python would hold scope. The following were our test functions, entered on the Chrome Developer Tools command line for javascript and the REPL for Python.
The output from both these tests are the same: x is defined. This is because both python and javascript lack block scope, something very much present in C++.
In the particular case I was discussing, the choice becomes a big more grey. There are strong opinions on both sides of the fence. At this point I agree with the accepted answer on the linked StackOverflow discussion: "For the case where a variable is used temporarily in a section of code, it's better to declare var in that section, so the section stands alone and can be copy-pasted." I think the use "var" can be as helpful for people as compilers, and it makes the intent of a block of code more clear. Using "var" on a variable in a code block says to me that this variable is intended for use in this code block, and may not have any meaning outside of it.
Thursday, July 25, 2013
A Nose Specific Django Settings File
The Python Nose test framework has a several advantages over Django's default test framework. I personally wanted to use Nose because of the xunit plugin which can output results in xunit format. I needed this output format so that test results could be interpreted by Bamboo, the continuous integration solution we're using at my work.
For using Nose with Django, the django-nose package is an obvious choice. However, it requires setting several settings values in your settings.py file that were only used by Nose. Also, I like being able to run my tests using Django's default test runner. I'm familiar with the output format, with the syntax for specifying how to run a single test, etc. and I didn't want to limit myself to running only Nose tests.
The approach I took was to create a new settings file for running nose tests. This would contain just the settings needed for configuring django-nose, and import the main settings file to fill in the rest. This is kind of the opposite of the usual pattern of importing a "local_settings.py" file at the bottom of your main "settings.py" to specify scenario specific settings (e.g. development vs production).
The comments in the script specify how to use it. I hope it's helpful.
For using Nose with Django, the django-nose package is an obvious choice. However, it requires setting several settings values in your settings.py file that were only used by Nose. Also, I like being able to run my tests using Django's default test runner. I'm familiar with the output format, with the syntax for specifying how to run a single test, etc. and I didn't want to limit myself to running only Nose tests.
The approach I took was to create a new settings file for running nose tests. This would contain just the settings needed for configuring django-nose, and import the main settings file to fill in the rest. This is kind of the opposite of the usual pattern of importing a "local_settings.py" file at the bottom of your main "settings.py" to specify scenario specific settings (e.g. development vs production).
The comments in the script specify how to use it. I hope it's helpful.
Subscribe to:
Posts (Atom)