Monday, March 12, 2012

Problems with identity column

I was working on a program recently that had a bit field called 'completed'
which recorded when a certain event was complete. I was removing this field
because it was possible to tell that the event was complete through other
means so the field was a duplication and a violation of good data design.
The end result was that it was possible for something in the program to show
as completed when in fact it wasn't. I thought this was a really good
example of how bad data design effects the end user.
When using Identity columns as the primary key some here think it is bad
design but I've never seen anyone quote an actual divantage for the end
user, all the "divantages" seem to be quite abstract and far removed from
the real world. In fact all I can see is advantages for the end user. Can
anyone come up with an actual real word divantage for an identity column
pk?IDENTITY, BIT, and "field" are **totally non-relational** concepts.
But in fairness, you realized that you had a computed column (NOT
field!!) so perhaps you can be saved.
Let me go ahead one more step and play Q&A with the direction I think
you are going:
Q: Couldn't a compound key become very long?
A1: So what? This is the 21-s's century and we have much better
computers than we did in the 1950's when key size was a real physical
issue. What is funny to me is the number of idiots who replace a
natural two or three integer compound key with a huge GUID that no
human being or other system can possibly understand because they think
it will be "faster and easy" to program.
A2: This is an implementation problem that the SQL engine can handle.
For example, Teradata is an SQL designed for VLDB apps that uses
hashing instead of B-tree or other indexes. They guarantee that no
search requires more than two probes, no matter how large the database.
A tree index requires more and more probes as the size of the database
increases.
A3: A long key is not always a bad thing for performance. For example,
if I use (city, state) as my key, I get a free index on just (city). I
can also add extra columns to the key to make it a super-key when such
a super-key gives me a covering index (i.e. an index which contains all
of the columns required for a query, so that the base table does not
have to be accessed at all).
A handle to the row? Oh, you mean faking a sequential file's
positional record number, so I can reference the physical storage
location? Sure, if I want to lose all the advantages of an abstract
data model, SQL set oriented programming, carry extra data and destroy
the portability of code!
More and more programmers who have absolutely no database training are
being told to design a database. They are using GUIDs, IDENTITY, ROWID
and other proprietary auto-numbering "features" in SQL products to
imitate either a record number (sequential file system mindset) or OID
(OO mindset) since they don't know anything else.
Experienced database designers tend toward intelligent keys they find
in industry standard codes, such as UPC, VIN, GTIN, ISBN, etc. They
know that they need to verify the data against the reality they are
modeling. A trusted external source is a good thing to have.
The IDENTITY column is a holdover from the early programming languages
which were very close to the hardware. For example, the fields (not
columns; big difference) in a COBOL or FORTRAN program were assumed to
be physically located in main storage in the order they were declared
in the program. The languages have constructs using that model --
logical and physical implementations are practically one! The data has
meaning BECAUSE of the program reading it (i.e. the same bits could be
a character in one program and be an integer in another)
The early SQLs were based on existing file systems. The data was kept
in physically contiguous disk pages, in physically contiguous rows,
made up of physically contiguous columns. In short, just like a deck
of punch cards or a magnetic tape. Most programmer still carry that
mental model, which is why I keep doing that rant about file vs. table,
row vs. record and column vs. field.
But physically contiguous storage is only one way of building a
relational database and it is not the best one. The basic idea of a
relational database is that user is not supposed to know *how* or
*where* things are stored at all, much less write code that depends on
the particular physical representation in a particular release of a
particular product on particular hardware at a particular time.
One of the biggest errors is the IDENTITY column (actually property,
not a column at all) in the Sybase/SQL Server family. People actually
program with this "feature" and even use it as the primary key for the
table! Now, let's go into painful details as to why this thing is bad.
The first practical consideration is that IDENTITY is proprietary and
non-portable, so you know that you will have maintenance problems when
you change releases or port your system to other products. Newbies
actually think they will never port code! Perhaps they only work for
companies that are failing and will be gone. Perhaps their code is
such crap nobody else want their application.
But let's look at the logical problems. First try to create a table
with two columns and try to make them both IDENTITY. If you cannot
declare more than one column to be of a certain data type, then that
thing is not a datatype at all, by definition. It is a property which
belongs to the PHYSICAL table, not the LOGICAL data in the table.
Next, create a table with one column and make it an IDENTITY. Now try
to insert, update and delete different numbers from it. If you cannot
insert, update and delete rows from a table, then it is not a table by
definition.
Finally create a simple table with one IDENTITY and a few other
columns. Use a few statements like
INSERT INTO Foobar (a, b, c) VALUES ('a1', 'b1', 'c1');
INSERT INTO Foobar (a, b, c) VALUES ('a2', 'b2', 'c2');
INSERT INTO Foobar (a, b, c) VALUES ('a3', 'b3', 'c3');
To put a few rows into the table and notice that the IDENTITY
sequentially numbered them in the order they were presented. If you
delete a row, the gap in the sequence is not filled in and the sequence
continues from the highest number that has ever been used in that
column in that particular table. This is how we did record numbers in
pre-allocated sequential files in the 1950's, by the way. A utility
program would then "pack" or "compress" the records that were flagged
as deleted or unused to move the empty space to the physical end of the
physical file. IDENTITY leaves the gaps.
But now use a statement with a query expression in it, like this:
INSERT INTO Foobar (a, b, c)
SELECT x, y, z
FROM Floob;
Since a query result is a table, and a table is a set which has no
ordering, what should the IDENTITY numbers be? The entire, whole,
completed set is presented to Foobar all at once, not a row at a time.
There are (n!) ways to number (n) rows, so which one do you pick? The
answer has been to use whatever the *physical* order of the result set
happened to be. That non-relational phrase "physical order" again!
But it is actually worse than that. If the same query is executed
again, but with new statistics or after an index has been dropped or
added, the new execution plan could bring the result set back in a
different physical order.
Can you explain from a logical model why the same rows in the second
query get different IDENTITY numbers? In the relational model, they
should be treated the same if all the values of all the attributes are
identical.
Using IDENTITY as a primary key is a sign that there is no data model,
only an imitation of a sequential file system. Since this "magic,
all-purpose, one-size-fits-all" pseudo-identifier exists only as a
result of the physical state of a particular piece of hardware at a
particular time as read by the current release of a particular database
product, how do you verify that an entity has such a number in the
reality you are modeling?
You will see newbies who design tables like this:
CREATE Drivers
(driver_id IDENTITY (1,1) NOT NULL PRIMARY KEY,
ssn CHAR(9) NOT NULL REFERENCES Personnel(ssn),
vin CHAR(17) NOT NULL REFERENCES Motorpool(vin));
Now input data and submit the same row a thousand times, a million
times. Your data integrity is trashed. The natural key was this:
CREATE Drivers
(ssn CHAR(9) NOT NULL REFERENCES Personnel(ssn),
vin CHAR(17) NOT NULL REFERENCES Motorpool(vin),
PRIMARY KEY (ssn, vin));
To demonstrate, here is a typical newbie schema -- you will them all
over the news groups. I call them "idiots" because they always name
the IDENTITY property column "id" in EVERY table. They don't
understand basic data modeling -- one and only name for an attribute.
About half the time they don't use any DRI, but let's show it.
CREATE TABLE MotorPool
(id IDENTITY (1,1) NOT NULL PRIMARY KEY,
ssn CHAR(9) NOT NULL REFERENCES Personnel(snn),
vin CHAR(17) NOT NULL REFERENCES Vehicle(vin));
CREATE TABLE Personnel
(id IDENTITY (1,1) NOT NULL PRIMARY KEY,
ssn CHAR(9) NOT NULL UNIQUE,
.);
CREATE TABLE Vehicles
(id IDENTITY (1,1) NOT NULL PRIMARY KEY,
vin CHAR(17) NOT NULL UNIQUE,
..);
Now change a row in Personnel:
UPDATE Personnel
SET ssn = '666666666'
WHERE id = 1;
or
UPDATE Personnel
SET ssn = '666666666'
WHERE ssn = '999999999';
or
BEGIN ATOMIC
DELETE FROM Personnel WHERE id = 1;
INSERT INTO Personnel VALUES ('666666666');
END;
What happened in Motorpool? There is no logical relationship between
the real key and the exposed physical locator, not is there any way to
make one.
Another cute way to destroy data integrity:
BEGIN ATOMIC
DELETE FROM Foobar
WHERE id = <<some row>>;
INSERT INTO Foobar
VALUES ( <<recreate deleted row>> )
END;
Logically this should do nothing, but since IDENTITY has gaps, it
trashes the data.
Now you are REALLY thinking about relations and keys instead of 1950's
sequential record numbering. Adding an IDENTITY column to either of
these tables as a candidate key would be dangerously redundant; one
query uses the IDENTITY and another uses the real key, and like a man
with two watches, you are never sure what time it is.
Finally, an appeal to authority, with a quote from Dr. Codd:
"..Database users may cause the system to generate or delete a
surrogate, but they have no control over its value, nor is its value
ever displayed to them ..."(Dr. Codd in ACM TODS, pp 409-410) and Codd,
E. (1979), Extending the database relational model to capture more
meaning. ACM Transactions on Database Systems, 4(4). pp. 397-434.
This means that a surrogate ought to act like an index; created by the
user, managed by the system and NEVER seen by a user. That means never
used in queries, DRI or anything else that a user does.
Codd also wrote the following:
"There are three difficulties in employing user-controlled keys as
permanent surrogates for entities.
(1) The actual values of user-controlled keys are determined by users
and must therefore be subject to change by them (e.g. if two companies
merge, the two employee databases might be combined with the result
that some or all of the serial numbers might be changed.).
(2) Two relations may have user-controlled keys defined on distinct
domains (e.g. one uses social security, while the other uses employee
serial numbers) and yet the entities denoted are the same.
(3) It may be necessary to carry information about an entity either
before it has been assigned a user-controlled key value or after it has
ceased to have one (e.g. and applicant for a job and a retiree).
These difficulties have the important consequence that an equi-join on
common key values may not yield the same result as a join on common
entities. A solution - proposed in part [4] and more fully in [14] -
is to introduce entity domains which contain system-assigned
surrogates. Database users may cause the system to generate or delete
a surrogate, but they have no control over its value, nor is its value
ever displayed to them...." (Codd in ACM TODS, pp 409-410).
References
Codd, E. (1979), Extending the database relational model to capture
more meaning. ACM Transactions on Database Systems, 4(4). pp. 397-434|||Although I do use identity columns myself, off the top of my head, I can thi
nk
of four issues with Identity columns
1. Users have to make a call to the database to get the PK value. This is a
pain
in parent-child situations where the parent must save before you save the
children. It is much nicer from a development standpoint to be able to gener
ate
a unique PK value on the client (or middle-tier) side. This is an easy issue
to
solve of course, it just doesn't make it as nice.
2. It is difficult if not impossible to share your keys with third parties.
If
you need to allow third-parties to generate keys (e.g. shipping numbers), th
en
Identity columns are out of the question. They absolutely will need the abil
ity
to generate a unique key on their side that will be unique on your side. Thi
s is
situation where a check digit of some sort is truly necessary. However, most
systems do not have this requirement. If it is never expected for this situa
tion
to arise, then this shouldn't be a consideration. Not every system has to be
designed to meet DOD standards.
3. Replication is more difficult. If you have multiple sites with databases
that
contain Identity columns, it is much more difficult to set it up in such a w
ay
as to not get conflicts between the two sites. It is still doable, but more
problematic. In addition, the more sites you add, the more difficult it is.
4. It is more difficult to port to a system that does not contain Identity
columns. This one is a pipe dream IMO. Part of the cost of porting to anothe
r
system would be the creation of a new key generation mechanism to take the p
lace
of the identity column. Further, it is likely that there are far more issues
that will need to be resolved than handling identity column.
Part of the problem is that "primary key" denotes two vastly different conce
pts.
In the relational model, a key is a subset of an entity's attributes that a
human can use to identify that entity. However, in every vendor's DBMS, the
PK
is also the "glue" used to relate entities to each other. I.e., it has speci
al
meaning and purpose.
Further, I would argue that there are rare situations where a human has no n
eed
of identifying a entity. Generally, this happens when a human treats a serie
s of
items as a set of indistinguishable elements. For example, suppose we have
invoices and invoice items. While it is true that users will want a means to
identify an invoice, they may never have a desire to uniquely identify the i
tems
on the invoice and may specifically want to allow for situations where the s
ame
product is listed twice on the same invoice (A friend of mine actually
encountered this situation. I believe the reason given was clarity in displa
ying
discounts). However, while the *users* may never have a need to identify an
invoice item, the *system* that retrieves and saves them most likely will
(granted you could always read and write the entire list each time).
That said, when using Identity columns, I will make an effort to include at
least one unique index on the attributes other than the PK. The advantage of
this approach is that you can change and/or add unique keys far easier than
changing the makeup of the PK itself.
Thomas|||"--CELKO--" <jcelko212@.earthlink.net> wrote in message
news:1117151361.768467.270510@.z14g2000cwz.googlegroups.com...
> Q: Couldn't a compound key become very long?

> IDENTITY, BIT, and "field" are **totally non-relational** concepts.

> The first practical consideration is that IDENTITY is proprietary and
> non-portable,

> A handle to the row? Oh, you mean faking a sequential file's
> positional record number

> The IDENTITY column is a holdover from the early programming languages
<snip>
You proved my point perfectly. I asked if you could come up with an actual
real world end user divantage to identity pks. As I expected you came up
with a whole lot of abstract points that wouldn't affect the end user. I'm
sure that was a big cut and paste and largely irrelevant to the actual
question so I didn't read the whole thing but if by some chance there was a
divantage in there please let me know, I'd be very interested to see it.
Michael|||"Thomas Coleman" <replyingroup@.anywhere.com> wrote in message
news:ungSrBlYFHA.584@.TK2MSFTNGP15.phx.gbl...
> Although I do use identity columns myself, off the top of my head, I can
> think of four issues with Identity columns
> 1. Users have to make a call to the database to get the PK value. This is
> a pain in parent-child situations where the parent must save before you
> save the children.
Users wouldn't see this though as you could make the save into a single
operation.

> It is much nicer from a development standpoint to be able to generate a
> unique PK value on the client (or middle-tier) side. This is an easy issue
> to solve of course, it just doesn't make it as nice.
The uniqueidentifier solves this. By "identity pk" in my original Q I really
meant non-natural pk, either identity or guid or just an int, which I know I
should have stated :-)
> 2. It is difficult if not impossible to share your keys with third
> parties. If you need to allow third-parties to generate keys (e.g.
> shipping numbers), then Identity columns are out of the question. They
> absolutely will need the ability to generate a unique key on their side
> that will be unique on your side. This is situation where a check digit of
> some sort is truly necessary. However, most systems do not have this
> requirement. If it is never expected for this situation to arise, then
> this shouldn't be a consideration. Not every system has to be designed to
> meet DOD standards.
In that case I would drop the identity but keep it as an int.

> 3. Replication is more difficult. If you have multiple sites with
> databases that contain Identity columns, it is much more difficult to set
> it up in such a way as to not get conflicts between the two sites. It is
> still doable, but more problematic. In addition, the more sites you add,
> the more difficult it is.
Can you still replicated on your unique index?

> 4. It is more difficult to port to a system that does not contain Identity
> columns. This one is a pipe dream IMO. Part of the cost of porting to
> another system would be the creation of a new key generation mechanism to
> take the place of the identity column. Further, it is likely that there
> are far more issues that will need to be resolved than handling identity
> column.
True.

> That said, when using Identity columns, I will make an effort to include
> at least one unique index on the attributes other than the PK. The
> advantage of this approach is that you can change and/or add unique keys
> far easier than changing the makeup of the PK itself.
I agree with that, you should still have something unique that identifies
each records.
Michael|||> Users wouldn't see this though as you could make the save into a single
> operation.
Using stored procedures makes this a snap, or it would be easy in the
dynamic SQL generation to use the data entered as the key to retrieve the
row:
insert into table (col1, col2) values (1,2)
select pkValue from table where col1 = 1 --assuming it is the natural key.

> The uniqueidentifier solves this. By "identity pk" in my original Q I
> really meant non-natural pk, either identity or guid or just an int, which
> I know I should have stated :-)
The problem is that it is so darn big. I mean it will take four times as
much space as an integer! Meaning four times as many pages in your index,
and even worse if you use it in your clustered index. Why is it so hard to
just get the value back after you create the new row? It is easy to do
using the natural key of the row you have created, or scope_identity() (of
course this is assuming you are using a surrogate)

Here you are really mixing paradigms. The identity value should not be the
key you share. The key you share would be the third-party key. It gives
you a lot of freedom to modify the value later without shifting around
structures in your database, or even to have more than one third party
reference to the same row. Identities or any surrogate key with no
check-digit or something(as you continue on to discuss) generally make lousy
keys. because they are kind of plain, and too easy to mistake. It is best
when you can type in a value 00003002 not to be able to fat finger it and
type 00003001 and get an actual value that works. Not impossible to stumble
upon a value, but unlikely.
I think that either type of key works just fine, but I like identies for the
flexibility of building the natural keys, as well as the ability to have a
common pattern of development (all primary keys int, all primary keys
singular, etc.) I don't think this is a requirement for a good design, not
at all. But it is a good design and works great.
> I agree with that, you should still have something unique that identifies
> each records.
>
Unless there is a compelling reason not to, you should ALWAYS do this! This
is what really kills so many databases that use identity keys. Hopefully I
am preaching to the choir, but it bears repeating that a surrogate key must
be a surrogate for something.
There are cases where you can't come up with a unique key, but they are very
few and far between. The best example I have is logging operations that
occur closer together than .003 seconds (the grain of the datetime.)
----
Louis Davidson - http://spaces.msn.com/members/drsql/
SQL Server MVP
"Michael C" <mculley@.NOSPAMoptushome.com.au> wrote in message
news:%23SVYvUlYFHA.2756@.tk2msftngp13.phx.gbl...
> "Thomas Coleman" <replyingroup@.anywhere.com> wrote in message
> news:ungSrBlYFHA.584@.TK2MSFTNGP15.phx.gbl...
> Users wouldn't see this though as you could make the save into a single
> operation.
>
> The uniqueidentifier solves this. By "identity pk" in my original Q I
> really meant non-natural pk, either identity or guid or just an int, which
> I know I should have stated :-)
> In that case I would drop the identity but keep it as an int.
>
> Can you still replicated on your unique index?
>
> True.
>
> I agree with that, you should still have something unique that identifies
> each records.
> Michael
>|||>> Users wouldn't see this though as you could make the save into a single
> Using stored procedures makes this a snap, or it would be easy in the dyna
mic
> SQL generation to use the data entered as the key to retrieve the row:
> insert into table (col1, col2) values (1,2)
> select pkValue from table where col1 = 1 --assuming it is the natural key.
Granted. Didn't say it wasn't possible; just more of a pain. It's more of a
pain
with the parent-child scenario like a invoice and invoice items. You have to
save the invoice, retrieve the new key and then save the invoice items. Not
a
big deal and I have used stored procs to solve this problem easily. Just mor
e of
a pain.
<snip>
> Why is it so hard to just get the value back after you create the new row?
It
> is easy to do using the natural key of the row you have created, or
> scope_identity() (of course this is assuming you are using a surrogate)
It isn't *so* hard per se. Just more of a pain. Like I said, if you have a l
ong
inheritance chain (parent-child-child...), it makes it more of a pain. Not
impossible to overcome. Just more problematic.

> Here you are really mixing paradigms. The identity value should not be th
e
> key you share.
<snip>
Exactly. The identity value in and of itself cannot be shared to a third par
ty.
Another unique key must be derived that is given to the third party (as you
said) that contains check digits and such. I have used this exact schema in
situations where a bar code with a check digit was necessary. A new key was
derived *once it was realized* that it would be needed. The bar coded value
was
merely one more way to identify the entity.
<snip>

> Unless there is a compelling reason not to, you should ALWAYS do this! Th
is
> is what really kills so many databases that use identity keys. Hopefully
I am
> preaching to the choir, but it bears repeating that a surrogate key must b
e a
> surrogate for something.
> There are cases where you can't come up with a unique key, but they are ve
ry
> few and far between. The best example I have is logging operations that o
ccur
> closer together than .003 seconds (the grain of the datetime.)
Agreed or that the exact make up the unique key is not known at initial desi
gn.
The lesson here is to strive to be the most restrictive in your design. Try
to
apply unique indexes initially. Try to make columns non-nullable. Add check
constraints etc. By abstracting the key away from the data, you make it poss
ible
to remove these restrictions later or even add new ones.
Remember, that the original poster asked what *potential* downsides are ther
e to
an identity. They do exist. As with all design decisions, their use must be
evaluated using more criteria than just "Codd says don't use them!".
Thomas|||> Remember, that the original poster asked what *potential* downsides are
> there to an identity. They do exist. As with all design decisions, their
> use must be evaluated using more criteria than just "Codd says don't use
> them!".
True enough, I was just opining back because I just felt the rush of the
"don't let the db server do anything" mentality that seems to be so popular
these days (and often ruining good data everywhere when not done by
extremely professional people, due to the multitude of tools that let you
subtrovert the rules of the database if they are not implemented for every
modification, not just those going through an exterior layer.
----
Louis Davidson - http://spaces.msn.com/members/drsql/
SQL Server MVP
"Thomas Coleman" <thomas@.newsgroup.nospam> wrote in message
news:%23Hp5tDnYFHA.3272@.TK2MSFTNGP14.phx.gbl...
> Granted. Didn't say it wasn't possible; just more of a pain. It's more of
> a pain with the parent-child scenario like a invoice and invoice items.
> You have to save the invoice, retrieve the new key and then save the
> invoice items. Not a big deal and I have used stored procs to solve this
> problem easily. Just more of a pain.
> <snip>
> It isn't *so* hard per se. Just more of a pain. Like I said, if you have a
> long inheritance chain (parent-child-child...), it makes it more of a
> pain. Not impossible to overcome. Just more problematic.
>
> <snip>
> Exactly. The identity value in and of itself cannot be shared to a third
> party. Another unique key must be derived that is given to the third party
> (as you said) that contains check digits and such. I have used this exact
> schema in situations where a bar code with a check digit was necessary. A
> new key was derived *once it was realized* that it would be needed. The
> bar coded value was merely one more way to identify the entity.
> <snip>
>
> Agreed or that the exact make up the unique key is not known at initial
> design. The lesson here is to strive to be the most restrictive in your
> design. Try to apply unique indexes initially. Try to make columns
> non-nullable. Add check constraints etc. By abstracting the key away from
> the data, you make it possible to remove these restrictions later or even
> add new ones.
> Remember, that the original poster asked what *potential* downsides are
> there to an identity. They do exist. As with all design decisions, their
> use must be evaluated using more criteria than just "Codd says don't use
> them!".
>
> Thomas
>|||Michael,
First, I can think of three good uses for IDENTITY columns:
1) Provide a search key for full-text search
2) Small applications that very well could be done in Access
3) Non "database" usage -- logging tables, error tables, etc.
The single largest reason not to use an IDENTITY, GUID, or any other
meaningless key is that they become POINTERs to data. There are inherent
problems with pointer-based data models that the relational data model set
out to solve. If you ever research implementation of Heirarchical (IMS) or
Network (CODASYL) models (which both are pointer-based), you'll see why this
is so problematic.
The whole point of the relational model is that data inherently relate to
eachother and need no pointers to tie them together. When you have
Order_Nums, Part_Nums, Codes, etc, you don't need a pointer -- you already
have something that uniquely identifies the entity.
Just like any improvement in technology, you can choose to ignore it and go
with how they used to do it. They built houses just fine in the 40's with
knob & tube wiring, and if you built a house today with it, I'm sure it woul
d
work ok. But professionals build houses with Romex and grounding because
those improvements solved a helluva lot of problems that N&T have. Your new
N&T house would probably start to have issues if you try to draw too many
amps and will become a safety hazard after a few decades.
As such, if you choose to go with pointer-based data model, it will face the
same problems that other pointer-based models have. And it will fall apart.
Alex Papadimoulis
http://weblogs.asp.net/Alex_Papadimoulis
"Michael C" wrote:

> I was working on a program recently that had a bit field called 'completed
'
> which recorded when a certain event was complete. I was removing this fiel
d
> because it was possible to tell that the event was complete through other
> means so the field was a duplication and a violation of good data design.
> The end result was that it was possible for something in the program to sh
ow
> as completed when in fact it wasn't. I thought this was a really good
> example of how bad data design effects the end user.
> When using Identity columns as the primary key some here think it is bad
> design but I've never seen anyone quote an actual divantage for the end
> user, all the "divantages" seem to be quite abstract and far removed fr
om
> the real world. In fact all I can see is advantages for the end user. Can
> anyone come up with an actual real word divantage for an identity colum
n
> pk?
>
>|||> [...] Just more of a pain.

> It isn't *so* hard per se. Just more of a pain. Like I said, if you have a
long
> inheritance chain (parent-child-child...), it makes it more of a pain.
The right way is usually the harder way. I have yet to find anything where
this is not true -- shortcuts create problems in everything.
I don't quite understand the lazy programmer. I can understand a lazy
carpenter -- lifting wood, sawing, etc is, quite literally, hard work. But
with a programmer, we're talking keystrokes and a few mouse clicks. What
exactly are we avoiding here?
Alex Papadimoulis
http://weblogs.asp.net/Alex_Papadimoulis

No comments:

Post a Comment