CodeSOD: Getting Overloaded With Details |
Operator overloading is one of those "dangerous" features. Like multi-parent inheritance, it can potentially create some really expressive, easy to read code, or it can create a disaster of incomprehensible nonsense.
In C++, many core classes use operator overloading, most notably the I/O classes, which reuse (or abuse) the bitshift operators into stream operators. So, for example, one possible way of converting a string into an integer might be to do something like this:
bool str2val(const string &s, int &v) {
std::istringstream iss(s);
istream &i = (iss >> v);
bool b = (!(i.fail() || i.bad())) && iss.eof();
return b;
}
As I'm reconstructing this example from our submitter's notes, any error in this code is mine.
This particular example converts a string
into an istringstream
- wrapping an input stream around a string. Then, using the >>
operator, attempts to read the contents of that stream into an integer variable. This, again, is one of those operator overloads. It also returns an istream
object, which we can then use to check the status of the conversion.
Now, this particular example comes from Gaetan, who didn't implement it because it was the best way to convert strings to integers. Instead, this is a compromise solution, that I included first so that we can understand what the code is supposed to do.
Gaetan's interaction with this code started, as so many legacy projects do, with "it doesn't compile." A fifteen year old code block, which had compiled just fine under GCC 4.8.0 stopped compiling under GCC 5.5.0.
Gaetan pulled up the code, and found this:
bool str2val(const string &s, int &v) {
std::istringstream iss(s);
bool b = iss.operator >>(v).operator void*() != 0 && iss.eof();
return b;
}
This code does the same thing as the above block, but without any hint of readability. Instead of using the overloaded operator as an operator, the original developer instead invokes it like a function, and then invokes a conversion to void*
, which is utter nonsense. In fact, it was that specific conversion which GCC 5+ balked on, but which GCC 4.8.0 did not mind in the least. The comparison against 0
was just a hack-y way to see if the fail
/bad
bits were set.
There's no reason to write the code this way, except perhaps because you have a really deep understanding of C++ syntax and really want to show it off. Gaetan is not a C++ wizard, so when re-implementing this code in a way that compiled, they did their best to re-implement it as close as possible to the original implementation.
Just, y'know, in a way that was readable, that compiled, and didn't depend on some type-punning magic to work.
https://thedailywtf.com/articles/getting-overloaded-with-details
Метки: CodeSOD |
CodeSOD: Contractor Management System |
Maximillion was hired to maintain a PHP CMS. His new employer, up to this point, had just been contracting out work, but as time went on, contracting rates got higher, the amount of time required to add basic features kept going up. It was time to go ta a full time employee.
"The system's pretty simple," the hiring manager explained during the interview, "as I understand it, it's basically just one PHP file."
It was not just one PHP file, but the manager wasn't that far off. The front page of the site was 4.9MB. That's not 4.9MB rendered, that's the PHP file. PHP and HTML existed side by side with in-line JavaScript and in-line CSS.
And the code had some… treats.
//strip content of extra tinyMCE tags
$offset=0;
$content = " ".$content;
while(($pos = @stripos($content,'
',$offset))) {
$content = substr($content,0,$pos).substr($content,$pos+13);
$offset = $pos+13;
}
// doing this twice seems to get all of them out;
$offset=0;
$content = " ".$content;
while(($pos = @stripos($content,'
',$offset))) {
$content = substr($content,0,$pos).substr($content,$pos+13);
$offset = $pos+13;
}
"Doing this twice seems to get all of them out". I get that TinyMCE, a WYSIWYG editor, might inject some noise into the generated HTML, and that you might want to clean out those
tags, but why twice? The comment implies its necessary, but why?
Of course, no giant PHP file would be complete without reimplementing date logic.
public function time_from($timestamp){
$difference = time() - $timestamp;
$periods = array("sec", "min", "hour", "day", "week", "month", "years", "decade");
$lengths = array("60","60","24","7","4.35","12","10");
if ($difference > 0) { // this was in the past
$ending = "ago";
} else { // this was in the future
$difference = -$difference;
$ending = "to go";
}
for($j = 0; $difference >= $lengths[$j]; $j++) $difference /= $lengths[$j];
$difference = round($difference);
if($difference != 1) $periods[$j].= "s";
$text = "$difference $periods[$j] $ending";
return $text;
}
Honestly, and it terrifies me to say this, while I've got a lot of problems with this function, it doesn't bother me as much as usual. Since the goal is to just sorta ballpark with "4 weeks ago" all the bad date assumptions don't matter that much. I still hate it.
And while the contractors clearly understood what functions were, that doesn't mean all of the contractors did. For example, this block:
$offset = 0;
while($pos = @stripos($string,'src=',$offset)) {
$first_quote = @stripos($string,'"',$pos+3);
$second_quote = @stripos($string,'"',$first_quote+2);
$first_3 = strtolower(substr($string,$first_quote+1,3));
if($first_3=='www'||$first_3=='htt'||$first_3=='mai') {
if($first_3=='www') $string = $core->run_action('pages','str_insert',array('http://',$string,$first_quote+1));
}
else $string = $core->run_action('pages','str_insert',array($url_fix,$string,$first_quote+1));
$offset = $second_quote+1;
}
That just gets copy-pasted any time they need to do any URL handling. What, do you think you get to 4.9MB of source code by writing reusable code? You gotta copy/paste to get those numbers up!
https://thedailywtf.com/articles/contractor-management-system
Метки: CodeSOD |
CodeSOD: Are You Active Enough? |
Cornelius was working with some code where the objects might be "active" or "inactive". His code needed to do something different, depending on whether the objects were "active" or not, but fortunately, there was a handy-dandy IsActive
method. Weirdly, that method required a bool
parameter, but also returned a bool
. Since there wasn't any useful documentation, Cornelius checked the C++ code.
bool SomeActivatedClass::IsActive(bool& active)
{
active = true;
return false;
}
This method leaves the choice of whether or not the object IsActive
up to the caller. If you check the value in the pass-by-reference parameter, it will always be true
. If you check the return value, it will always be false
.
This method is, truly, a work of art. It doesn't tell you, the audience, what to think about the nature of being active. It instead poses a question, and lets the caller decide. It forces us to confront our own preconceptions about an IsActive
method, the nature of return values, and why we sometimes prefer reference parameters. Like films by the the great auteur, Neil Breen, it asks big questions even as it provides no answers of its own.
Метки: CodeSOD |
Error'd: Scratch and Dent |
Renault driver Oisin H. writes "I had no idea French week had only six days" It seems they too have never got the hang of Thursdays.
For your breakfast, please enjoy the lovely shade of egg yolk in this submission from Yaytay who highlights "It's so broken that it's showing the default .Net error page!"
Since we're making today "Pick on Microsoft Day", here's a treat from Azure. Rob F. shares "The view as web page divided by zero and gave me a computer says no response." Go easy on Rob, Google's translator has a beef with Microsoft, too.
And a reader who styles himself TheRealSteveJudge queries "Where can I get a 0-bit Windows? At least I can download drivers for it."
Finally, loyal reader Pascal apparently files his taxes the same time we do. 2017, 2018, Thursdays, whatever.
Метки: Error'd |
CodeSOD: A Little Info |
Matt has plans for the next few years: dealing with the "inheritance" of some legacy systems.
They're written in VB.Net, which isn't itself a problem, but the code quality leaves just a bit to be desired.
Dim FileInfo As New System.IO.FileInfo(FileName)
FileName = FileInfo.Name
If FileName = "" Then
Throw New ApplicationException("cannot determine the file name for '" & FileName & "'")
End If
I suspect they wanted to check if the file exists. Which is definitely a reasonable thing to want to check with aFileInfo
object, which is why it has an Exists
property which one could use. But this code doesn't accomplish the exists check- if the file doesn't exist, the Name
property is whatever name you used to construct the object. Perhaps they hoped to canonicalize the name, but that's also not a thing the Name
property does.
So, in practice, this code doesn't do anything, but boy, it looks like it might be an important check.
Метки: CodeSOD |
CodeSOD: A World Class Programmer |
Jesse had a "special" co-worker, Rupert. Rupert was the sort of person that thinks they're the smartest person walking the Earth today, and was quite happy to loudly proclaim that everyone else is wrong. Rupert was happy so long as everyone else was ready to bask in his "genius".
Fortunately for Jesse, Rupert left, because he'd received a huge offer for a senior developer role at a different company. Everyone at Jesse's company privately chuckled about it, because this is the kind of code Rupert's genius produced:
protected function getStreet($street) {
return $street;
}
protected function getCity($city) {
return $city;
}
protected function getState($state) {
return $state;
}
protected function getZip($zip) {
return $zip;
}
Jesse writes:
Rupert, who wrote these methods had the title of senior developer, and was arrogant about it to boot. I could even forgive someone making a mistake like this once, meaning to remove it, and forgetting about it as it got buried within a larger project, but he wrote four methods all of which do nothing and then called them over and over again. He even had the unmitigated gall to say to the entirety of the development team that he was a "world-class coder" at one point. Apparently in his mind "world-class" means "often writes code that does literally nothing."
Well, Rupert's brought his world-class talents to a new employer, so good luck to them. They'll need it.
Метки: CodeSOD |
CodeSOD: As Authentic as it Gets |
Virginia N (previously) needed to maintain some authentication logic. The actual rules for authentication weren't actually documented, so her choices were to either copy/paste some authentication code from a different project, or try and refactor the authentication method in this one.
It was a hard choice. We'll dump the whole block of code, but I want to pull out a few highlights, starting with the opening condition:
if (LDAP == "O" && txtPassword.Text.ToUpper() != "MASTER PASSWORD. ABSOLUTELY SECRET, SAFE AND SECURE")
Oh yeah, hard-coded passwords in the application source. We're off to a good start. And that condition gets repeated:
if (LDAP != "O" || txtPassword.Text.ToUpper() == "MASTER PASSWORD. ABSOLUTELY SECRET, SAFE AND SECURE" || user == null)
Or wait, what if they actually supplied a user password, maybe?
if (
LDAP != "O" && System.Text.Encoding.ASCII.GetString(user.USR_PWD) ==
Encryption.Encrypt(txtPassword.Text)
||
txtPassword.Text.ToUpper() == "MASTER PASSWORD. ABSOLUTELY SECRET, SAFE AND SECURE"
|| sMDPass != null && sMDPass.Length > 0 && sMDPass == Encryption.Encrypt(txtPassword.Text)
|| isLoginAuto && txtUserName.Text.ToUpper().Equals(username.ToUpper()) &&
txtPassword.Text.Length == 0
|| ldapauth)
Indenting from the original. At least they store passwords encrypted, I suppose. Just, y'know, not the master password. Also note the blend of &&
s and ||
s- I'm not 100% sure what this is supposed to do, but I hope the order of operations is correct.
frmBaseMessageBox.Show(new MLangue().GetLibelle(11649), MessageBoxButtons.OK,
MessageBoxIcon.Stop);
I "love" when application logic and showing message boxes gets mixed together. It really gives you a sense of what the developer was going through, in a stream-of-consciousness fashion. There is no separation of concerns. There are a bunch of similar lines.
Now, those are a few ugly highlights, but the real fun is in seeing the whole thing. It's about three hundred lines of nested if
s with gigantic conditions, loads of different paths for attempting to handle logins, all implemented as a click event handler. Like a mountain, you can't really appreciate its scale till you stand back and see it all in context.
private void cmdOK_Click(object sender, EventArgs e)
{
Config.TraceOut.WriteLine("TimeTest:cmdOK_Click start:" + DateTime.Now.ToString("HH:mm:ss"));
bool exiting = false;
bool UserNbIncremented = false;
try
{
Cursor = Cursors.WaitCursor;
pictureBox1.Cursor = Cursors.WaitCursor;
Refresh();
string connectionName = SetConnInfo();
string filePathOrURL = txtLAN.Text;
if (filePathOrURL.Length == 0) filePathOrURL = Application.StartupPath;
string LDAP = Param.GetParametre("LDAP");
bool ldapauth = false;
SUSERSInfo user = null;
if (LDAP == "O" && txtPassword.Text.ToUpper() != "MASTER PASSWORD. ABSOLUTELY SECRET, SAFE AND SECURE")
{
Config.TraceOut.WriteLogLine("before LDAP auth");
bool isaldapauth = false;
if (txtPassword.Text.Length == 0)
{
isaldapauth = false;
}
else
{
DirectoryEntry entry = null;
SearchResult sr = null;
isaldapauth = LDAPAuthenticate(txtUserName.Text, txtPassword.Text, ref entry, ref sr);
Trace.Write("after LDAP auth ");
}
Config.TraceOut.WriteLogLine(isaldapauth ? "ok" : "not ok");
if (isaldapauth)
{
IRelationPredicateBucket filter = new RelationPredicateBucket();
filter.PredicateExpression.Add(PredicateFactory.CompareValue(BIGTABLEWITHANYKINDOFDATAYOUDONTKNOWWHERETOSAVEFieldIndex.COD_TYPE,
ComparisonOperator.Equal, 194));
filter.PredicateExpression.Add(PredicateFactory.CompareValue(BIGTABLEWITHANYKINDOFDATAYOUDONTKNOWWHERETOSAVEFieldIndex.COD_ID,
ComparisonOperator.Equal, 0));
object objdeslong =
BLFunctions.GetFieldValue(EntityFieldFactory.Create(BIGTABLEWITHANYKINDOFDATAYOUDONTKNOWWHERETOSAVEFieldIndex.COD_DESLONG), filter);
if (!BLFunctions.IsNull(objdeslong) && objdeslong.ToString().IndexOf("|") > 0 &&
objdeslong.ToString().Substring(0, objdeslong.ToString().IndexOf("|")).Trim() == "O")
{
MAJUserFromLDAP(txtUserName.Text, txtPassword.Text);
user = new ApplicationLogic().AuthenticateUser(filePathOrURL, connectionName,
txtUserName.Text.Trim(), txtPassword.Text, true);
}
else
{
user = new ApplicationLogic().AuthenticateUser(filePathOrURL, connectionName,
txtUserName.Text.Trim(), txtPassword.Text, true);
Trace.Write("after db auth");
Config.TraceOut.WriteLogLine(user != null ? "ok" : "not ok");
}
ldapauth = user != null;
}
}
if (LDAP != "O" || txtPassword.Text.ToUpper() == "MASTER PASSWORD. ABSOLUTELY SECRET, SAFE AND SECURE" || user == null)
{
user = new ApplicationLogic().AuthenticateUser(filePathOrURL, connectionName, txtUserName.Text.Trim(), txtPassword.Text, isLoginAuto && txtUserName.Text.Equals(username));
}
ACCESCODE_NBRESSAI acces = new ACCESCODE_NBRESSAI("");
ApplicationLogic applogic = new ApplicationLogic();
if (user != null)
{
if (!CheckUserAccess(user.USR_ID,applogic, ref acces))
{
Cursor = Cursors.Default;
pictureBox1.Cursor = Cursors.Default;
Refresh();
SUSERSEntity userent= GetUserEnt(user.USR_ID);
int nbmin = 0;
if (userent != null)
{
nbmin =Convert.ToInt32(Math.Floor( (userent.USR_DATINVALID.AddMinutes(acces.Temps) - DateTime.Now).TotalMinutes));
}
frmBaseMessageBox.Show(new MLangue().GetLibelle(12397).Replace("%%", nbmin.ToString()), MessageBoxButtons.OK, MessageBoxIcon.Stop);
return;
}
}
if (null != user && !(isLoginAuto && txtUserName.Text.Equals(username) && !isInDom))
{
IRelationPredicateBucket filter = new RelationPredicateBucket();
string sMDPass = Param.GetParametre("MDPASS");
if (
LDAP != "O" && System.Text.Encoding.ASCII.GetString(user.USR_PWD) ==
Encryption.Encrypt(txtPassword.Text)
||
txtPassword.Text.ToUpper() == "MASTER PASSWORD. ABSOLUTELY SECRET, SAFE AND SECURE"
|| sMDPass != null && sMDPass.Length > 0 && sMDPass == Encryption.Encrypt(txtPassword.Text)
|| isLoginAuto && txtUserName.Text.ToUpper().Equals(username.ToUpper()) &&
txtPassword.Text.Length == 0
|| ldapauth)
{
ResetNbEssai(user.USR_ID, applogic);
if (!CheckUserSign(user.SDEM_SIGN)) return;
ApplicationMainConnection.DbConnectionName = connectionName;
MLangue.LangID = ApplicationLogic.User.USR_LANGID;
MLangue.LoadAllLibellesFromDB();
AppSettings.SaveConfig(txtUserName.Text,
optLAN.Checked ? ConnectionType.LAN : ConnectionType.WS,
txtLAN.Text, "", DatabaseServerType.SqlServer, cboConnection.Text,
Convert.ToString(cboLang.Value), AppSettings.LastDate);
if (RightsManager.UserNotActivated())
{
Cursor = Cursors.Default;
pictureBox1.Cursor = Cursors.Default;
Refresh();
frmBaseMessageBox.Show(new MLangue().GetLibelle(4089), MessageBoxButtons.OK,
MessageBoxIcon.Stop);
return;
}
else
{
DbVersionInfo dbvers = new ApplicationLogic().GetCurrentBDVersion();
if (dbvers == null)
{
Cursor = Cursors.Default;
pictureBox1.Cursor = Cursors.Default;
Refresh();
frmBaseMessageBox.Show(new MLangue().GetLibelle(11649), MessageBoxButtons.OK,
MessageBoxIcon.Stop);
return;
}
if (!VersionSync())
{
Cursor = Cursors.Default;
pictureBox1.Cursor = Cursors.Default;
Refresh();
frmBaseMessageBox.Show(new MLangue().GetLibelle(9653), MessageBoxButtons.OK,
MessageBoxIcon.Stop);
return;
}
else
{
if (!ChangeMDP(user.USR_ID, applogic,new MLangue()))
{
DialogResult = DialogResult.Cancel;
ThreadAutoResetEvent.Set(); // Signaling to the main thread to continue
return;
}
IEntityField2 field = EntityFieldFactory.Create(V_MODIFCODFieldIndex.ID);
field.AggregateFunctionToApply = AggregateFunction.CountDistinct;
IRelationPredicateBucket filter1 = new RelationPredicateBucket();
filter1.PredicateExpression.Add(
PredicateFactory.CompareValue(V_MODIFCODFieldIndex.TRT_EXEC,
ComparisonOperator.Equal, "N"));
filter1.PredicateExpression.Add(
PredicateFactory.CompareValue(V_MODIFCODFieldIndex.CONST_DISABLE,
ComparisonOperator.Equal, "O"));
object obj = BLFunctions.GetFieldValue(field, filter1);
int count = 0;
if (!BLFunctions.IsNull(obj))
count = Convert.ToInt32(obj);
if (count > 0)
{
Cursor = Cursors.Default;
pictureBox1.Cursor = Cursors.Default;
Refresh();
frmBaseMessageBox.Show(new MLangue().GetLibelle(10572), MessageBoxButtons.OK,
MessageBoxIcon.Stop);
return;
}
dbCodeLibelle.SDEM_DBCODLIBELLE =
BLFunctions.GetDemandeurForUser().SDEM_DBCODLIBELLE;
cmdOK.Enabled = false;
cmdCancel.Enabled = false;
Cursor = Cursors.WaitCursor;
pictureBox1.Cursor = Cursors.WaitCursor;
exiting = true;
Refresh();
DialogResult = DialogResult.OK;
ThreadAutoResetEvent.Set(); // Signaling to the main thread to continue
}
}
}
else
{
SUSERSEntity userentity = null;
IncrementNbEssai(user.USR_ID, applogic, acces, ref userentity);
UserNbIncremented = true;
if (userentity.USR_NBR == 0)
{
frmBaseMessageBox.Show(new MLangue().GetLibelle(12396), MessageBoxButtons.OK, MessageBoxIcon.Error);
CloseApp();
}
else
{
if (LDAP == "O" && !ldapauth)
frmBaseMessageBox.Show(new MLangue().GetLibelle(9978), MessageBoxButtons.OK,
MessageBoxIcon.Error);
else
frmBaseMessageBox.Show(sMessageInvalidPass, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
Cursor = Cursors.Default;
pictureBox1.Cursor = Cursors.Default;
txtUserName.Focus();
}
}
else
{
Cursor = Cursors.Default;
pictureBox1.Cursor = Cursors.Default;
SUSERSEntity userentity = null;
if(user!=null)
IncrementNbEssai(user.USR_ID, applogic, acces, ref userentity);
UserNbIncremented = true;
if (userentity!=null&&userentity.USR_NBR == 0)
{
frmBaseMessageBox.Show(new MLangue().GetLibelle(12396), MessageBoxButtons.OK, MessageBoxIcon.Error);
CloseApp();
}
else
{
if (LDAP == "O" && !ldapauth)
{
frmBaseMessageBox.Show(new MLangue().GetLibelle(9978), MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
else
{
if (!(isLoginAuto && isLoginAutoA && isInDom))
frmBaseMessageBox.Show(new MLangue().GetLibelle(7478) + " !", MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
isLoginAutoA = false;
txtUserName.Focus();
}
if (++tries > 3 && !UserNbIncremented)
{
CloseApp();
}
}
catch (Exception e1)
{
if (e1.InnerException.InnerException != null &&
e1.InnerException.InnerException.Message.IndexOf("None of the factories") >= 0)
{
frmBaseMessageBox.Show(
"Suite `a la mise `a jour du fichier de configuration, veuillez relancer l'application afin de prendre en compte les modifications");
Cursor = Cursors.Default;
DialogResult = DialogResult.Cancel;
}
else
{
Cursor = Cursors.Default;
DialogResult = DialogResult.Cancel;
new MyCompanyUICommonFunctions().HandleError(e1, this);
}
}
finally
{
if (!exiting)
{
Cursor = Cursors.Default;
}
else
{
CaptureScreen();
UICommonApplicationManager.ShowSplash(memoryImage, cboLang.Value);
}
}
}
Virginia opted to refactor this instead of copy/pasting from another project. The other project was written in VB.Net, and while the code was slightly better organized, it was in VB.Net. By preference, Virginia opted to stick with even this C#. And honestly, given the organization this comes from, I can't imagine that the VB.Net was much cleaner.
Метки: CodeSOD |
CodeSOD: Classic WTF: All Pain, No Gain |
It's a holiday here in the states. So enjoy this classic from the far off year of 2009. --Remy
"My company has very strict policy on direct access to the database," Steve writes, "no hand-built SQL in the front-end code and always use a stored procedure to access the data. The reasoning behind this was simple and sound: Avoid SQL injection attacks and increase database performance. "
"The execution, however, was not so simple and sound. They went through all the pain of needing to use stored procs, but none of the gain. I'll leave it as an exercise to the reader to see why."
Create Procedure [dbo].[Authenticate] @StartDate datetime, @EndDate datetime, @UserID numeric(18,0), @Password char(50), @DatabaseName char(50) AS Declare @strSQL char(2000) Set @strSQL = ''Select count(c.ID) as Count, sum(sc.Total) as Users'' + '' from Users sc '' + '' inner join UserRelationship pr on pr.PartyIDChild = sc.OrganizationID '' + '' inner join Batch b on b.BatchID = sc.BatchID '' + '' inner join '' + ltrim(rtrim(@DatabaseName)) + ''.dbo.Users c on sc.UserID= c.ID '' + '' where b.SentDate between '''''' + ltrim(rtrim(@StartDate)) + '''''''' + '' and '''''' + ltrim(rtrim(@EndDate)) + '''''''' + '' and c.UserID = '' + ltrim(rtrim(str(@UserID))) + “and c.Password = “ + ltrim(rtrim(str(@Password))) Exec (@strSQL)
Steve continues, "the most amazing thing is that when I pointed this out to the senior developer who designed this, he didn't understand what the problem was, and therefore management wouldn't even talk to me about it. Thankfully, I don't work there anymore."
https://thedailywtf.com/articles/classic-wtf-all-pain-no-gain
Метки: CodeSOD |
Error'd: Not Very Clever |
In this process of collecting your submissions, the single most common category has been string conversions of NaN, null, and undefined. They are so common, I've become entirely bored with them. Date conversions, however, still do amuse a bit. Or will do. Or will did? Will have did? In any case, here's another installment of wibbly bits. They may not be clever, but some are a little funny.
Competitive programmer Laks may have got the answer technically incorrect, but he did it REALLY fast. Shouldn't he have won on speed? Says Laks "It turns out you can implement flux capacitor in software."
AT&T subscriber Bruce W. notes "In Samsung Smart Home, time is not linear." Keep an eye out for localized time eddies, Bruce!
Old-timer Adam R. got a late delivery. "I forgot all about this desk I ordered back in the disco era. It took 48 years but it did eventually arrive." It may have got stuck in one of Bruce's eddies.
Also dabbling in the shallows of those eddies, Ziga Sancin asks "Standard shipping, please." Good, fast, cheap, pick...
Finally, Todd R. unearths a relic. "This Jira ticket was created early in the reign of the Roman emperor Nero. Hopefully someone will get around to it soon!"
Метки: Error'd |
CodeSOD: A More Functional Approach |
In functional programming languages, you'll frequently see some variation of the car
/cdr
operations from LISP. Given a list, these functions can split it into the head (or first element) of the list, and the tail (the rest of the list). This is often used to traverse a list recursively: function f
performs an operation on the head of the list, and then recursively calls f
on the tail of the list.
I bring this up because we're talking about C# today. C# has plenty of fun functional programming features, and you can certainly use them to write very clean, comprehensible code. Or, like Nico's co-worker, you could do something like this.
private void HandleProcessPluginChain(ProcessMessage message, List processPlugins )
{
if (processPlugins.Any())
{
processPlugins.First().Process(message, (msg) =>
{
HandleProcessPluginChain(msg, processPlugins.Skip(1).ToList());
});
}
}
public class EveryProcessPlugin : IProcessPlugin
{
public void Process(ProcessMessage message, Action onProcessCompleted )
{
...
onProcessCompleted(message);
}
}
Let's start with the EveryProcessPlugin
class. As you can see, it performs some process, and then calls an onProcessCompleted
callback handler. That looks inoccuous, but is already pretty ugly. This code is synchronous, which means there's no reason to pass a callback- the way we get notified that the process has finished is that the method returns. Perhaps they wanted to expand this so that there could be async processes in the future, but that isn't what's happening here. Plus, C# has better constructs for handling async operations, and has had them for awhile.
But the real pain is in the HandleProcessPluginChain
. Here, they've adapted the car
/cdr
approach to C#. If there are any elements in the list, we pop the first one off, and call its Process
method. The callback we pass is a recursive reference to HandleProcessPluginChain
where we Skip
the first element (a C# cdr
) to pass the tail of the list to the recusive call.
Key language features that make this approach efficient in functional languages don't exist here. C# doesn't support tail-call optimizations, so even if the compiler could see that this was tail calls (I'm not certain about that, with the lambda in there), C# wouldn't benefit from that anyway. The fact that we need to pass a List
and not an IEnumerable
means that every call to ToList
is visiting every member in the list to construct a new List
object every time.
Maybe this is a case where someone was coming from F#, or Haskell, or wished they were using those languages. The fact that it's not conventional C# isn't itself terrible, but the fact that it's trying so hard to be another language is.
Метки: CodeSOD |
CodeSOD: Without a Map, Without a Clue |
Ali was working on an app for a national government. The government provided its own mapping API for its own custom mapping services. It did not provide any documentation, and the only "sample" code was hitting "view source" on an existing map page on the government's websites.
Ali writes: "I was going through their own implementations, looking for something that would help, when I stumbled upon this gem. I think it speaks for itself, no?"
var mapType;
var mapURL;
function addMapService(type,url)
{
mapType = type;
mapURL = url;
performAdd();
}
function performAdd()
{
try
{
setTimeout("index.addMapService(mapType,mapURL);",2000);
}
catch(err)
{
if(err.name=="TypeError")
performAdd();
}
}
No, Ali, this absolutely does not speak for itself, because I have a lot of questions. So many questions.
addMapService
populates some global variables and then calls performAdd
, because gods forbid that we pass parameters. performAdd
passing a string of JavaScript code, which invokes index.addMapService
and passes the global variables, and schedules that execution for two seconds in the future.
Why? I've heard of lazy loading, but this is a bit ridiculous.
Now, it's important to note that setTimeout
couldn't possibly throw an exception in this example, but it's okay, because if it does for some reason, we'll… just do the same thing. I guess with another two second delay. And since we're using global variables, I guess maybe the value could change before this code executes, so retrying the same action time and time again until it works… might actually work. It probably won't do what you expect, but it'll do something.
So no, I don't think this speaks for itself, and honestly, whatever it's trying to say, I wish it would stop.
https://thedailywtf.com/articles/without-a-map-without-a-clue
Метки: CodeSOD |
CodeSOD: Making a Weak Reference |
There are times when performance absolutely matters. And the tech lead that Janell was working with (previously) was absolutely certain that their VB.Net project needed to be "designed for performance". Performance was so important that in the final design, they used a home-brewed JSON database (which, at its lowest level, was just a JSON file on disk), and it took 7 round-trips to their home-brewed database to read a single record.
Despite all this attention on performance, the system had a memory leak! As we've covered in the past, garbage-collected languages can have leaks, but they may take a little more effort than the old-school versions. Janell fired up a debugger, looked at the memory utilization, and started trying to figure out what the problem was.
The first thing Janell noticed was that there were millions of WeakReference
objects. A WeakReference
can hold a reference to an object without preventing garbage collection. This is the sort of thing that you might use to prevent memory leaks, ensuring that objects get released.
A little more poking revealed two layers to the problem. First, every business object in the application inherited from BaseObject
. Not the .NET object
type that's the base class for all objects, but a custom BaseObject
. Every class had to inherit from BaseObject
.
Public Class BaseObject
Public Sub BaseObject()
' Snip
Memory.Register(Me)
' Snip
End Sub
End Class
Buried in this custom constructor which hooked all sorts of inner-platform extensions into the base class of all classes, was that Memory.Register
call.
Public Class Memory
Private Shared memory As LinkedList(Of WeakReference(Of BaseObject))
Public Shared Sub Register(obj As BaseObject)
memory.AddLast(New WeakReference(Of BaseObject)(obj))
End Sub
' Snip
End Class
Once again, the tech lead wanted to "validate performance", and one of the ways they did this was to track all the business objects that were ever created. By using a WeakReference
, they guaranteed that all the actual business objects could still be cleaned up… but nothing ever cleaned up the WeakReference
objects themselves.
Janell fixed the leak in the simplest way possible: by deleting the Memory
class and any reference to it.
Метки: CodeSOD |
Who Tests the Tester? |
The year was 2001: the year before many countries in the EU switched from using their national currency to the new euro. As part of the switch, many financial software packages had to be upgraded. Today's submitter, Salim, was hired as an IT support staffer in a medium-sized healthcare organization in the Netherlands. While Salim had a number of colleagues, they had to support a greater number of small satellite offices, and so on occasion any of them would be left to hold the main office alone. It just so happened that Salim's first solo day was the day they were testing the software upgrade, with the CFO himself executing the test.
The manager of IT had prepared well. Every aspect of the test had been thought through: the production server had been cloned into a fresh desktop computer, placed in the server room, set apart from the other servers so that the CFO wouldn't accidentally touch the production server. To make it stand out, rather than a rack they'd placed the computer on one of those thin little computer desks that were in fashion at the time, a little metal contraption with a drawer for the keyboard and wheels so it could be moved. The setup got an office chair and a phone, for comfort and so that the CFO could summon help if needed. The CFO had been given step-by-step instructions from the software vendor, which he and the manager had gone over ahead of time. All the bases were covered. Hopefully.
Early in the morning the day of the test, the CFO walked to the support desk and asked for access to the server room. Salim walked him to the room, unlocked the door for him, and pointed him to the computer desk. Before he left, he pointed out the phone and gave the CFO his direct extension in case anything went wrong. The CFO thanked him, and Salim left him to it.
No sooner had he sat down than the call came: the CFO asking for help. Although he had a good deal of general IT knowledge, Salim hadn't personally inspected any of the instructions and didn't know much about the software. So he walked back to the server room, anxiety growing.
Arriving in the server room, Salim found the CFO standing next to the computer desk. Salim sat down, pulled the keyboard out from under the monitor, and flicked on the screen. Summoning up his most professional voice, he asked, "Right, how can I help?"
"Ah ... thanks," came the reply. "I can manage from here."
Salim writes: "To this day, I do not know if his problem was not being able to find the keyboard, or switch on the monitor."
Метки: Feature Articles |
Error'd: Board Silly |
Baby Boomer Eric D. accidentally shares the prototypical Gen X experience, this time via Pella windows. "I guess I can't actually buy stuff because I'm in the unlucky 55-64 age band." Welcome to irrelevance, Eric!
Regular contributor Pascal has found another failure of Google's online calculator. "Apparently, the number line has changed slightly since I went to school." Mathematica it ain't.
Tester Andrew H. tentatively notes "I'm not sure if that's a good score or not. But at least the test completed without errors....oh!"
An anonymous race critic opines "The only way to make EuroNASCAR worse is to implement the timing graphics in Excel."
Carl who still uses Facebook says "I clicked Like, and the error message speaks for itself." Hey, it's not wrong.
Ordinarily I wood avoid submissions where the WTF isn't obvious, but I'm making an exception for this item from reader Tim R.. It's a head scratcher. He writes "Gmail's 'undo send' feature is really useful. Usually after using it, it takes you back into draft mode with the email open. In this instance, after a couple of 'reloading...' dialogs, I just got this picture of a fence. The email was just plain text with no attachments so I have no idea where the picture came from."
Метки: Error'd |
CodeSOD: Subline Code |
Have you ever needed to prune an order line? Y'know, prune them. This is an entirely common operation you do on orders all the time, and requires absolutely no context or explanation. There's absolutely no chance that you might need to wonder, exactly what is being pruned.
Bartholomew A. was a little bit confused by the prune
method in the codebase. Fortunately, reading through the code makes everything clear:
// Prune
// @param d - Expects 'd' to be OrderDetails Line Item Row
prune = (function prune(d, re) {
var z = [],
y = [],
out = [],
sub = {};
if (Object.prototype.toString.call(re) != "[Object String]") {
re = "SubLines";
match = { 'SubLines': [] };
} else {
match[re] = [];
}
// Get the SubLines
out = _.filter(d, _.matches(match, false));
_.each(out, function (i) {
var x;
x = _.pick(i, [re]);
[].push.apply(y, x.SubLines);
});
// trim it down to just Tracking
_.each(y, function (value, key) {
if (!_.has(value, 'Tracking')) {
return false;
}
z = [].concat.apply(z, value.Tracking);
});
// return some data
return z;
});
There, that clears everything up. Now you know what pruning is, and I hope you can explain it to me, because I'm pretty confused. If the input parameter d
is actually just a "Line Item Row" why are we filter
ing it? Is this using the "lodash" library, and if so, have they hacked the matches
method, because according to the docs, it doesn't take a boolean parameter. Maybe it did in an old version?
I think that by the end of this, it will return an array of all of the tracking information for every sub-line on this order-line, which I have to admit, I've worked on a lot of order management systems, and we never broke line items up into sub-lines, because the whole point of a line item was that it was the atom of your order, but sure, whatever.
Bartholomew provides more context:
I saved this from a few years ago at a company where I used to work. It sits in a 3,000 line AngularJS file at no particular level of abstraction. It involves order lines and sublines, which indicates that it relates to the business entities displayed on the screen, and then it "prunes" them. I have no idea what that means, but if this doesn't get executed something doesn't work.
This file just grew larger and larger. There were no unit tests. Something was always broken and the answer was always adding more and more code…
Метки: CodeSOD |
CodeSOD: Meaningful Article Title |
There was an era where UI designer tools output programmatic code representing that UI. So, say, if you were dragging and dropping objects in the Windows Forms designer in Visual Studio, the output would be C# code. Now, the designer would have some defaults. When you added a new button, it might identify it as button15
. Of course, you wouldn't leave that name as it was, and you'd give it a meaningful name. That name would then become the property name of that field in the class generated by the designer.
Well, you might give it a meaningful name. Gormo inherited some Windows Forms code, which challenges the meaning of "meaningful names".
private System.Windows.Forms.ComboBox _cb_PG_PG_K;
private System.Windows.Forms.ComboBox _cb_LPVB_V_K;
private System.Windows.Forms.ComboBox _cb_LPVB_W;
private System.Windows.Forms.ComboBox _cb_LPVB_K;
private System.Windows.Forms.ComboBox _cb_LPVB_TRO;
private System.Windows.Forms.ComboBox _cb_LPVB_Z;
private System.Windows.Forms.ComboBox _cb_LP_MV;
private System.Windows.Forms.ComboBox _cb_LP_AG;
private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.ComboBox _cb_LP_HB;
private System.Windows.Forms.ComboBox _cb_LP_GSGS;
private System.Windows.Forms.ComboBox _cb_LP_GSGN;
private System.Windows.Forms.ComboBox _cb_LP_SF;
private System.Windows.Forms.ComboBox _cb_LP_CBI;
private System.Windows.Forms.ComboBox _cb_LP_E;
private System.Windows.Forms.ComboBox _cb_LP_CAS;
private System.Windows.Forms.Button _btnQ_LP_MV;
private System.Windows.Forms.Button _btnQ_LP_AG;
private System.Windows.Forms.Button _btnQ_LP_HB;
private System.Windows.Forms.Button _btnQ_LP_GSGS;
private System.Windows.Forms.Button _btnQ_LP_GSGN;
private System.Windows.Forms.Button _btnQ_LP_SF;
private System.Windows.Forms.Button _btnQ_LP_CBI;
private System.Windows.Forms.Button _btnQ_LP_E;
private System.Windows.Forms.Button _btnQ_LP_CAS;
private System.Windows.Forms.Button _btnQ_LP_PK;
private System.Windows.Forms.Button _btnQ_LP_PG_K;
private System.Windows.Forms.Button _btnQ_LP_P;
private System.Windows.Forms.Button _btnQ_LP_P_K;
private System.Windows.Forms.Button _btnQ_LPB_PB_K;
private System.Windows.Forms.Button _btnQ_LBSV_BSV_K;
private System.Windows.Forms.Button _btnQ_cb_LV_V_K;
private System.Windows.Forms.Button _btnQ_cbLM_M_ID;
private System.Windows.Forms.Button _btnQ_LE_E;
private System.Windows.Forms.Button _btnQ_BHVS_BV;
private System.Windows.Forms.Button _btnQ_BHVS_BV_K;
private System.Windows.Forms.Button _btnQ_BSTVS_BST_K;
private System.Windows.Forms.Button _btnQ_PVB_W;
private System.Windows.Forms.Button _btnQ_PVB_K;
private System.Windows.Forms.Button _btnQ_PVB_TRE;
private System.Windows.Forms.Button _btnQ_PVB_TRO;
private System.Windows.Forms.Button _btnQ_PVB_Z;
private System.Windows.Forms.Button _btnQ_PVB;
private System.Windows.Forms.Button _btnQ_PVB_PVB_K;
private System.Windows.Forms.Label label18;
private System.Windows.Forms.Label label13;
private System.Windows.Forms.PictureBox pictureBox2;
private System.Windows.Forms.PictureBox pictureBox1;
private System.Windows.Forms.CheckBox _chkb_Filtern;
private System.Windows.Forms.ComboBox _cbLM_M_ID;
private System.Windows.Forms.ComboBox _cb_LP_PK;
private System.Windows.Forms.ComboBox _cbLM_N;
private System.Windows.Forms.ComboBox _cb_PG_PG;
private System.Windows.Forms.ComboBox _cb_LPB_PB_K;
private System.Windows.Forms.ComboBox _cb_LPB_PB;
private System.Windows.Forms.ComboBox _cb_LV_V;
private System.Windows.Forms.ComboBox _cb_LV_V_L;
private System.Windows.Forms.ComboBox _cb_LBSV_BSV_K;
private System.Windows.Forms.ComboBox _cb_LBSV_BSV;
private System.Windows.Forms.ComboBox _cb_BSTVS_BST_K;
private System.Windows.Forms.ComboBox _cb_LPVB_PVB;
private System.Windows.Forms.ComboBox _cb_BSTVS_BST;
private System.Windows.Forms.ComboBox _cb_LPVB_PVB_K;
private System.Windows.Forms.ComboBox _cb_BHVS_BV_K;
private System.Windows.Forms.ComboBox _cb_BHVS_BV;
private System.Windows.Forms.Label _labelcb;
private System.Windows.Forms.ComboBox _cb_M;
private System.Windows.Forms.ComboBox _cb_LE_E;
private System.Windows.Forms.Button _btn_Export;
private System.Windows.Forms.CheckBox _chkb_M;
private System.Windows.Forms.ComboBox _cb_LP_P;
private System.Windows.Forms.ComboBox _cb_LP_P_K;
private System.Windows.Forms.Button _btn_M;
private System.Windows.Forms.ToolTip toolTip1;
private System.Windows.Forms.RadioButton _rb_123;
private System.Windows.Forms.RadioButton _rb_ABC;
private System.Windows.Forms.Button _btnQ_PMGR_PM_G;
private System.Windows.Forms.Button _btnQ_PMGR_PM_G_K;
private System.Windows.Forms.ComboBox _cb_LPMGR_PM_G;
private System.Windows.Forms.ComboBox _cb_LPMGR_PM_G_K;
private System.Windows.Forms.CheckBox _chkbLPMG;
private System.Windows.Forms.CheckBox _chkb_A;
private System.Windows.Forms.ListBox _lb_ERG;
private System.Windows.Forms.CheckBox _chkb_LE;
private System.Windows.Forms.CheckBox _chkb_BHVS;
private System.Windows.Forms.CheckBox _chkb_BSTVS;
private System.Windows.Forms.CheckBox _chkb_LP;
private System.Windows.Forms.CheckBox _chkb_LPG;
private System.Windows.Forms.CheckBox _chkb_LBSTV;
private System.Windows.Forms.CheckBox _chkb_LPB;
private System.Windows.Forms.CheckBox _chkb_LV;
private System.Windows.Forms.CheckBox _chkb_LM;
private System.Windows.Forms.CheckBox _chkb_LPVB;
private System.Windows.Forms.RadioButton _rb_LM_Z;
private System.Windows.Forms.RadioButton _rb_LM_E;
private System.Windows.Forms.Panel _p_N;
private System.Windows.Forms.CheckBox _chkb_ERG_S;
private System.Windows.Forms.CheckBox _chkb_ERG_A;
private System.Windows.Forms.CheckBox _chkb_dbS;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.ComboBox _cb_LPVB_TRE;
private System.Windows.Forms.Button button2;
private System.Windows.Forms.Button button33;
private System.Windows.Forms.Button button31;
private System.Windows.Forms.Button button30;
private System.Windows.Forms.Button button29;
private System.Windows.Forms.Button button28;
private System.Windows.Forms.Button button27;
private System.Windows.Forms.Button button26;
private System.Windows.Forms.Button button25;
private System.Windows.Forms.Button button24;
private System.Windows.Forms.Button button23;
private System.Windows.Forms.Button button21;
private System.Windows.Forms.Button button19;
private System.Windows.Forms.Button button17;
private System.Windows.Forms.Button button15;
private System.Windows.Forms.Button button12;
private System.Windows.Forms.Button button10;
private System.Windows.Forms.Button button8;
private System.Windows.Forms.Button button7;
private System.Windows.Forms.Button button6;
private System.Windows.Forms.Button button5;
private System.Windows.Forms.Button button4;
private System.Windows.Forms.Button button3;
private System.Windows.Forms.ComboBox _cb_BSTVS_V_K;
private System.Windows.Forms.Button button11;
private System.Windows.Forms.ComboBox _cb_BHVS_V_K;
private System.Windows.Forms.Button button9;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Button btn_fieldNames;
So, what's worse than naming your combobox combobox1
? Naming it _cb_PG_PG_K
, which is exactly the sound I'd make if someone handed this code to me and told me I owned it now. I'm sure these names were all part of a coding scheme that was meaningful to the original developer, but they didn't write it down, so no one else knows what this genius scheme means, beyond the obvious Hungarian notation, like _cb
(marking something as a combo box). What does BHVS_V_K
mean beyond someone mashing random keys on the keyboard?
The real WTF, however, is clearly that this pile of fields on a form is a clear indicator that this is one of those kinds of UIs that fits the "your company's app" section of this classic comic.
Метки: CodeSOD |
CodeSOD: Mastery Through Repetition |
When I was a baby programmer, I subscribed to a computer magazine. In those olden times, these magazines would come with pages of program code, usually BASIC, so that you could type this program into your computer and run it. If you were careful about typos, you could accomplish quite a bit of "programming" without actually programming. What you were doing, in practice, was just typing.
One of Anthony's predecessors was quite the accomplished typist.
They needed to take the built-in .NET XmlDocument
class and add a one method to it. Now, C# offers a few techniques for doing this. The "traditional" object-oriented approach is to use inheritance. Now, that's not without its downsides, so C# also has the concept of "extension methods". This is a little bit of syntactic sugar, which allows you to declare static method that takes a parameter of XmlDocument
, but invoke it as if it were an actual instance method. Of course, depending on what you're doing, that might not give you all the functionality you need. And outside of those techniques, there are a number of the good-ol' Gang of Four design patterns, like the Visitor pattern which could solve this problem without loads of complexity. Or even just "a plain old static method" might fit.
But Anthony's predecessor didn't want to do any of those. They instead chose to use typing.
public XmlHelper()
{
doc = new XmlDocument();
}
public XmlHelper(string xml)
{
doc = new XmlDocument();
this.doc.LoadXml(xml);
}
public XmlHelper(XmlDocument doc)
{
this.doc = doc;
}
public void Attach(XmlDocument doc)
{
this.doc = doc;
}
public void LoadXml(string xml)
{
this.doc.LoadXml(xml);
}
public void LoadFromFile(string filePath)
{
this.doc.Load(filePath);
}
public XmlNode SelectSingleNode(string xpath)
{
return this.doc.SelectSingleNode(xpath);
}
public XmlNodeList SelectNodes(string xpath)
{
return this.doc.SelectNodes(xpath);
}
.... // another dozen similar overloads
public XmlNodeList GetFilteredNodeList()
{
return DoSomeFilteringOnMyNodeList(this.doc);
}
// another dozen of trivial overloads
}
The XmlHelper
class exposes all of the same methods as an XmlDocument
, but adds a single GetFilteredNodeList
. Because we didn't use inheritance, you can't slot an XmlHelper
in the same place as an XmlDocument
, but they both have the same interface (but don't actually implement a common Interface
). In short, this is a lot of code with no real benefit.
But I'm sure the developer was a very good typist.
Метки: CodeSOD |
CodeSOD: Authentic Mistakes |
John's employer needed a small web app built, so someone in management went out and hired a contracting firm to do the work, with the understanding that once it was done, their own internal software teams would do maintenance and support.
Fortunately for John's company, their standard contract included a few well-defined checkpoints, for both code quality audits and security audits. It's the last one that's relevant in this case.
There are three things you should never build for your application: date handling logic, encryption algorithms, or authentication mechanisms. These are all things that sound simple on the surface, but are actually quite difficult. You will mess them up, and you'll regret it. What's remarkable here, however, is seeing how badly one can mess up authentication:
$(document).ready(function() {
$("#password").val("");
$("#button").click( function() {
if($("#password").val() == ""){
showAdminInterface();
}
else
{
alert('Password not valid :(');
};
});
});
What you see here is client-side JavaScript. When the user clicks the wonderfully named #button
, we compare their #password
entry against… .
Not only are they storing the administrator password in plaintext in the database, they're dumping the admin password in the body of the document. Anyone can just hit "view source" and log in as an administrator.
Obviously, this failed the audit. "But," the contractor said, "it's perfectly safe, because we disabled right clicks, so no one can view source."
Shockingly, this still failed the audit.
Метки: CodeSOD |
Error'd: l'Audace |
Paul M. identifies an online vendor who isn't at all afraid of the bold ask. But he demurs. "Uh, I think I'll stick with the sale price thanks."
Clever Jim R. appears to have constructed a Jira workflow that includes a transition named "Cancel". Thus, when he chooses to "Cancel" an issue, he's presented with the option to continue to perform the Cancel operation or to cancel the Cancel. "Will I cancel the Cancel or will I cancel the issue?" he wonders. Readers, what do you suppose? Did Jim set this up on purpose just for the WTF?
Horolophile Alexander clocks in from next month with a quick word. "I thought Time Machine was only for Macs, but apparently you can use it with Apple Watch too."
Curious Peter wondered just what would happened if he posed the bold ask to Google. He found out. "Should we check for overflows in our calculator, Boss?"
Finally, the audacious
V C. shares this stumper, saying
"Azure's support for coming soon is coming soon. Hang tight."
I suspect
a setup, but I don't know how it was done. Toujours l'audace, VC.
Метки: Error'd |
News Roundup: Getting Real Phoney |
In the lead-up to the Chappelle Show sketch ‘When Keepin’ It Real Goes Wrong’, Dave Chappelle gives the following pretty great advice:
“It’s good to be real sometimes. It’s good to be phony sometimes. Yes, I said it. Phony! You think I’m this nice in real life? F$ck that son!”
We tend to stay real with the people when we benefit from telling the truth to them and hearing the truth from them, while we stay phony when we feel that we benefit from emotional investment.
Take a second to do an exercise splitting society into those that you should be “real” to/with and those that you should stay “phony” to/with. Here’s my take:
Be Real (to and with) | Stay Phony (to and with) |
Parents, Spouses, Young Children*, Teenagers | Extended Family |
Healthcare Workers | Celebrities + Self-Promoters |
Political candidates (someone desperately needs to get real with Andrew Yang) | Elected Officials + Business Leaders |
Co-workers | Bosses + Management |
(In Venkatesh Rao’s amazing six-part Gervais Principle breakdown of ‘The Office’ and management, he would argue that there lies a third column of people who think they are Real but actually Phony. It’s seriously worth your time to read the full piece. But for simplicity sake, I will keep these two columns.)
So where am I going with all this? Let’s talk about Basecamp.
First, the definition of a ‘basecamp’ is the following: a main encampment providing supplies, shelter, and communications for persons engaged in wide-ranging activities, as exploring, reconnaissance, hunting, or mountain climbing. What does a company who builds project management and communications tools, who changed their name from 37 Signals in 2014, have to do with adventure sports? Unclear.
Second, Basecamp had ~60 employees as of two weeks ago, but has written 5 books on management (!!). Basecamp must have the highest books written per employee rate of any company in history.
Oh yeah and third, management decided to get real with their employees by setting up a company-wide meeting to discuss banning political discussion in the office and 1/3 of their employees immediately accepted buyouts afterwards.
There are a lot of extremely smart pieces of the lead-up and aftermath of the debacle, so I recommend reading Casey Newton’s full piece at Platformer, Charlie Warzel’s piece at Galaxy Brain, and Vice’s piece.
But here’s simple take:
If Basecamp leadership had just stayed phony about their actual thoughts on names they couldn’t pronounce or quietly deleted the list that caused this whole debacle, none of this would have happened. Maybe the day they made this decision, they accidentally took down the silk-screened poster based on one of their bestsellers?
Last month we had some scary banking-related news out of Japan where data migration work had turned Mizuho Bank’s ATMs into debit-card-eating machines. This month we have some happy banking-related news out of Louisiana:
In a lawsuit filed against Ms. Spadoni in federal court in New Orleans, Charles Schwab said that it was supposed to have moved only $82.56 into Ms. Spadoni’s Fidelity Brokerage Services account, but that a software glitch had caused it to mistakenly transfer the seven-figure sum.’
The seven-figure sum in question totaled nearly $1.2 million and was immediately used to buy a house and car by the lucky recipient. Not bad for a quick 14602x ROI! For comparison, Dogecoin’s adjusted close low and high of the year are $.0057 and $.6848 resulting in a measly 145x ROI.
Sadly, the defendant could not keep her ill-gotten winnings:
Realizing the mistake, Schwab tried to reclaim the money through a computer system, but got a message that said “Cash not available,” the lawsuit said. A second attempt was also rejected, and Schwab received a message that said: “Insufficient funds, please work directly with the client to resolve.”
A Schwab employee called Ms. Spadoni four times but was unable to leave a message during two of those attempts because her answering machine was full, the lawsuit said. A corporate counsel for Schwab then called Ms. Spadoni at the Sheriff’s Office but was told that she was unavailable, according to the lawsuit, which said he then sent several text messages.
Mizuho, Citibank, and Chase really need to beef up their risk and IT teams.
Quick hits:
*I debated which category Young Children fall into, since ideas such as the ‘elf on the shelf’ incentivizes parents to use such narratives to keep their kids in line. However it is probably important to stay emotionally invested in your kids, so in the ‘real’ bucket they go.
Метки: News Roundup |