December 18, 2007
First off, Lucas Newman is leaving Delicious Monster -- as of January 1 he will be an iPhone engineer. This is an amazing opportunity for him, one I would never ask a friend to pass up. We remain buddies, although I'm running around Zoka these last couple weeks telling every girl I see that Lucas was secretly super-hot for her and is leaving now, which I think is starting to annoy him. Although, honestly, they'll probably all end up throwing themselves at him and he'll end up on top, again.
For those keeping score at home, this makes Mike Matas, Scott Maier, Tim Omernick, Drew Hamlin, and Lucas Newman that Apple has hired out of my employ. Yes, in fact, 100% of Delicious Monster's ex-employees are now working for Apple! You'd almost think Apple would start to pay me to train people for them. Oh, well. It's every kid's dream to work there, I can't say I blame them. Heck, I might work for Apple myself if they ever asked. And, like, wanted to give me EIGHTY ZILLION DOLLARS.
Also, seriously, if you want to work for Apple, you MIGHT want to, you know... GET TO KNOW ME.
Mike Lee is staying at Delicious Monster -- for now... DUM DUM DUM! You have to figure he's playing the various Apple teams off each other -- when you work at Delicious Monster, you don't jump for the girl that asks you to dance. Mike's all: "CoreAudio? Don't waste my time, sweetheart." "OS X Server? I'm sorry, you're not even getting an interview." "Ali Ozer and Scott Forstall got into a fistfight over me at lunch today? Now, see, these guys understand what kind of ball we are playing."
I realized tonight that I had yet another problem with CoreData, and it was a doozy, and not something where I could just put a hack on it. In fact, it was indicative of a fundamental architecture mismatch that I've been struggling with since I started this project.
So, this is a little vague, but I thought it might be important to document the process. Basically, when I bang up against a wall, I start looking bigger and bigger and bigger. Like, imagine I'm having trouble with a crumbling wall in an aqueduct -- my programmers brain does this: "Ok, why did I build this wall?" To keep the water in. "Why do I have water?" Because you need that to turn the water-wheel. "Is there some other way to turn it?" Not easily. "Why must it turn?" To power the grinder. "What needs grinding?" Corn. "Is there some other way to grind it?"
I'll get to truly huge things, where I start asking if the world even needs an app that catalogs books and DVDs and now boardgames when we could all be under five feet of water in a few years. Then it's time to take a nap and wake up and start again.
But my point is, you HAVE to question all the basic assumptions that led you to where you are, or you end up spending all your time writing the wrong code. I have always said that if you give me a perfectly spec'ed out program (one with a spec that can actually work, that I'm not going to have to modify as I go along), I can write that program for you in days. Always. The problem with coding is (a) fighting with frameworks, and (b) trying to figure out how the program should look, work, and interact even as we code it.
So we end up spending a lot of times fixing bugs in code that we really shouldn't have written in the first place -- code that doesn't really help the user, that just makes the app more complex, that is for a feature that never should have been put in, or is interacting with the user incorrectly and we're just putting spackle on a wall that's crumbling.
So here I am, tonight, running into my 1,000th bug with the fundamental mis-architecture in CoreData, which is that interacts with the UI layer and the disk layer / undo layer all using the same mechanism. They all rely on -didChangeValueForKey:, which is a huge mistake, because it means that, as a programmer, I can't sneak any data in -- I can't change a value without it creating an undo event.
Consider if, for example, I had a clock and its hands were CoreData objects. As they move forward through time, their position updates, so I'd tell them to update. And each time I did, an undo event would get pushed -- so the user actually could undo time.
This is obviously a contrived example, but it also points to the fundamental problem -- CoreData objects can't mix undoable and non-undoable changes.
So I've been struggling for three years now, trying to bend and hack and cajole CoreData's undo architecture into allowing me to do some actions synchronously and some asynchronously. (For instance, obviously, once the program has downloaded a cover from Amazon in a background thread, you don't want to UNDO the download -- it's not actually a state change, it's just a cache change -- yet, by default we end up with an undo event on the stack, in the MIDDLE of whatever the user is actually doing in the foreground.)
Fight fight gnash gnash complain complain. Tonight I hit on it. I needed to step back. Why isn't this working? Because undo wasn't designed this way in CoreData.
Well, I have undo in Delicious Library 1. It's not "magic" like with CoreData, but it works. In fact, now that I am thinking about it -- I've spent months and hundreds of lines of code trying to get CoreData's "magic" undo to work, when, in fact, there are really only FOUR actions that are ever undone:
1) Add a book -- undo to delete it
2) Delete a book -- undo to add it back
3) Change a property on a book, like its title or author -- undo to change it back
4) Make a loan -- undo to return the book
5) Return a book -- undo to re-make the loan
That's... about it. SO WHY HAVE I SPENT ALL THIS TIME TRYING TO GET COREDATA'S MAGIC SYSTEM TO WORK?
There's only five damn methods, at the top level, that need to participate in undo. It's pretty obvious I should be managing my OWN undoManager, turn off the one in CoreData, and just use CoreData for what it is EXTREMELY good at, which is minimal change tracking and fetching and storing data VERY VERY quickly.
Suddenly all these issues I've been having disappear. I don't have strange extra undo events on my stack when I fault in an object, because although CoreData might think my object changed, it's not driving the undo manager any more -- and when it goes to save, it's going to quickly discover there's no real substantive changes and just discard the whole event.
I don't have to try to work around some undo events by turning undo on and off, which required me to flush CoreData's transactions queue by hand, which was extremely sketchy because if you do it in some circumstances (eg, the middle of inserting a new object) the object will be corrupted.
I haven't started this yet -- I'll try it tomorrow. It's nice -- it'll pick up a bunch of the remaining issues I'm having in DL2, and should give us a good solid beta. The important thing here is, I was just too married to part of the code. I was so into using CoreData's magic undo that I kept going farther and farther to make it work, when I really needed to say, "Ok, this doesn't work in this situation, I'm doing my own undo in 40 lines of code."
I write the software for
I'm kept alive by Delicious Library
I'm kept alive by Delicious Library
- Mr. Murray is Dead.
- On saying goodbye.
- Movie Sequels We Need
- Video Game Ideas: iPhone SDK edition
- Open systems, closed systems, and the future of Ap...
- Video Game Idea: "Space 911"
- Go, NBC! You're SMURT!
- Bush & Sons, General Contractors.
- iPhone & iPod: contain or disengage?
- Video Game Idea: "The Jovian Infestation"
Pimp My Code
- Free Programming Tips are Worth Every Penny.
- I will insult your code!
- Part 1: Code Insults, Mark I
- Part 2: self = [stupid init];
- Part 3: Gradient TableViews
- Part 4: Returning late to return early
- JPEG2000: Cool but SLOW.
- Unit testing is teh suck, Urr.
- Part 5: Special Apple Sample Code Edition...
- Interlude: Free Code
- Pimp, Pimp Thyself.
- Frameworks are Teh Suck, Err.
- Part 6: The Pimp Before Christmas
- Thinking, boxes, & what kittens can do to them.
- Part 7: Pimplette?
- Part 8: Mary, Mary, why you buggin?
- Part 9: Beginner Code
- Part 10: Whining about Cocoa
- Part 11: This Sheet is Tight
- Part 12: Frozen in Carbonite
- Part 13: The Pimp Before Christmas, Redux
- Part 14: Be Inflexible!
- Part 15: The Greatest Bug of All
- Part 16: On Heuristics and Human Factors
- Part 17: Lost in Translations