Announcements: Meetup in Kansas City: Dinner and a Pint after KCDC |
The Kansas City Developer Conference is this week, followed by PubConf. Between these two events on Friday evening is plenty of time for a TDWTF dinner, and that's exactly what we're planning!
If you find yourself in Kansas City Missouri this Friday, for KCDC, PubConf, or perhaps because you live here, please come out to the Dubliner at 5:30 PM for dinner and a pint. I'll be there along with Martine Dowden and some TDWTF swag to give away. We'll talk software, discuss what we took away from the conference, and can head over to PubConf together.
If you would like to join us at 5:30 PM CT on Friday, July 19 please contact me at @mrdowden (Twitter) or drop me an email: Michael (at) Andromeda16.com
https://thedailywtf.com/articles/meetup-in-kansas-city-dinner-and-a-pint-after-kcdc
|
Метки: Announcements |
CodeSOD: Nothing Direct About directAddCartEntry |
It’s old hat, but every function, every class, every code unit you write, should all have a single, well-defined purpose. “Do one thing, and do it well,” as it were.
Of course, sometimes, it’s easier said that done, and mindlessly following that advice can lead to premature abstraction, and then you’ll have quite a mess on your hands. Still, it’s good advice, and a great design goal, even if you don’t head straight there.
Marigold found some code which, well, has a long way to go. A looooooong way to go.
directAddCartEntry = function (matnr, menge,updateByField,m,redu){
var that=this;
var produkt=new Object;
var target = document.getElementById('content');
spinner.spin(target);
if (produkt.BACK_PREIS!=undefined && produkt.BACK_PREIS!=""){
produkt.PREIS = produkt.BACK_PREIS
}
var Kundennummer = app.getModel("userData").getData().KUNDENNR;
var Land = app.getModel("userData").getData().LAND;
var Euland = app.getModel("userData").getData().ISTEULAND;
var Kundennummer_u_Euland = Kundennummer+"|"+Euland+"|"+Land;
var verpackungseinheit = "";
sap.ui.getCore().byId("app").getModel("kategorie").read("/PRODUKT_SET(MATNR='"+matnr+"',VKORG='"+VKORG+"',SPRAS='de',KAMPAGNE='"+Kundennummer_u_Euland+"',VTWEG='10')?$expand=MERKMAL",null,null,false,function(oData,response){
produkt=oData;
var mindestbestellmenge = produkt.BOMRABATT;
verpackungseinheit = produkt.VERPACKUNGSEINHEIT
if (mindestbestellmenge!="0.000 "&& mindestbestellmenge!="" && mindestbestellmenge != undefined){
mindestbestellmenge=mindestbestellmenge.split(".")[0]
var mindestbestellmenge = parseInt(mindestbestellmenge);
produkt.BOMRABATT=mindestbestellmenge
if (menge != mindestbestellmenge && vielfacher(menge,mindestbestellmenge)==false && redu!=true){
var dialog = new sap.m.Dialog({
showHeader: false,
content: [
new sap.ui.layout.HorizontalLayout({
content: [
new sap.m.Image({
src: "Image/helia_small.png",
}),
new sap.m.Text({
//text: sap.ui.getCore().byId("app").getModel("i18n").getProperty("dialogUsersonlyFooter")
}).addStyleClass('dialog__usersonlySmall'),
]
}).addStyleClass('dialog__usersonlyHeader'),
new sap.ui.layout.Grid({
hSpacing: 1,
vSpacing: 1,
layoutData: new sap.ui.layout.GridData({
span: "L12 M12 S12",
}),
content: [
new sap.ui.core.HTML({
layoutData: new sap.ui.layout.GridData({
span: "L12 M12 S12",
}),
content: verpackungseinheit=="ZS" ? sap.ui.getCore().byId("navContainer").getModel("i18n").getProperty("infozigarette") : sap.ui.getCore().byId("navContainer").getModel("i18n").getResourceBundle().getText("infonormal", produkt.BOMRABATT)
}).addStyleClass("dialog__usersonlyTitle"),
new sap.m.Select("dialogSelect",{
layoutData: new sap.ui.layout.GridData({
span: "L2 M2 S2",
}),
// items: productListItems2("zweier","","","")
items: productListItems4(null,null,produkt.MAXMENGE,produkt.BOMRABATT)
})/*.attachBrowserEvent(
"click",function(evt){
var _numProductsSelected = parseInt( this.getSelectedKey() );
var _i = +_numProductsSelected;
var plus=+_i;
this.destroyItems();
for (_i;_i<=999;_i=_i+plus){
// XXX
this.addItem(new sap.ui.core.ListItem({ text: _i, key: _i }))
}
this.setSelectedKey( _numProductsSelected );
}
).attachBrowserEvent(
"tap",function(evt){
var _numProductsSelected = parseInt( this.getSelectedKey() );
var _i = +_numProductsSelected;
var plus=+_i;
this.destroyItems();
for (_i;_i<=999;_i=_i+plus){
// XXX
this.addItem(new sap.ui.core.ListItem({ text: _i, key: _i }))
}
this.setSelectedKey( _numProductsSelected );
}
)*/,
new sap.m.Button({
text: "OK",
press: function(oEvent) {
var new_menge=sap.ui.getCore().byId("dialogSelect").getSelectedItem().mProperties.text
dialog.destroy();
directAddCartEntry (matnr, new_menge,updateByField,m)
simulateOrder()
directController.setPreiseundRabatte();
},
layoutData: new sap.ui.layout.GridData({
span: "L1 M1 S1",
})
}),
new sap.m.Button({
text: app.getModel("i18n").getProperty("pwdabort"),
press: function() {
dialog.destroy();
},
layoutData: new sap.ui.layout.GridData({
span: "L2 M2 S2",
})
}),
]
}),
//.addStyleClass('dialog__usersonlyChoice'),
]
}).addStyleClass('dialog__usersonlyChoice');
dialog.open()
spinner.stop();
return;
}
else{
if (istmindestmenge (oData,"","call")==true && redu!=true){
// var menge2=menge/2
var menge2=menge/oData.BOMRABATT;
directAddCartEntry (matnr, menge2,updateByField,m,true)
return
}
else{
produkt.MENGE = menge;
}
}
}else{
if (istmindestmenge (oData,"","call")==true && redu!=true){
// var menge2=menge/2
menge/oData.BOMRABATT;
directAddCartEntry (matnr, menge2,updateByField,m,true)
return
}
else{
produkt.MENGE = menge;
}
}
var Preis;
var model = m;
var data = model.getData();
var POSITIONEN = data.WK_POSITIONEN;
var POSITION = null;
var POSITIONIndex = -1;
if (produkt.BACK_PREIS!=undefined && produkt.BACK_PREIS!=""){
produkt.PREIS = produkt.BACK_PREIS
}
//Position suchen
for (var zxy = 0 ; zxy < POSITIONEN.length ; zxy ++) {
if (POSITIONEN[zxy].MATNR === produkt.MATNR) {
POSITION = POSITIONEN[zxy];
POSITIONIndex = zxy;
break;
}
}
//Wenn Position nicht gefunden, neu hinzuf?gen...
if (POSITION === null) {
produkt.PAKETPREIS = produkt.PREIS;
produkt.SPARPREIS = 0;
//Aktionsstaffelpreise und Staffelpreise ber?cksichtigen
var Preis = parseFloat(produkt.PREIS).toFixed(2);
if (produkt.STAFFELPREIS3!=""){
if( (parseFloat(produkt.MENGE) >= parseFloat(produkt.STAFFELPREIS6)) && ( parseFloat(produkt.STAFFELPREIS3) < parseFloat(produkt.PREIS) ) ) {
produkt.PAKETPREIS = produkt.STAFFELPREIS3;
produkt.SPARPREIS = ( parseFloat(produkt.PREIS) - parseFloat(produkt.STAFFELPREIS3));
var Preis = produkt.STAFFELPREIS3
}
}
if (produkt.AKTIONSPREIS3!=""){
if( (parseFloat(produkt.MENGE) >= parseFloat(produkt.AKTIONSPREIS6)) && ( parseFloat(produkt.AKTIONSPREIS3) < parseFloat(produkt.PREIS) ) ) {
produkt.PAKETPREIS = produkt.AKTIONSPREIS3;
produkt.SPARPREIS = ( parseFloat(produkt.PREIS) - parseFloat(produkt.AKTIONSPREIS3));
var Preis = produkt.AKTIONSPREIS3
}
}
if (parseFloat(produkt.AKTIONSPREIS)< parseFloat(produkt.PREIS) && produkt.AKTIONSPREIS!=""){
var Preis = parseFloat(produkt.AKTIONSPREIS).toFixed(2);
} // Vergleich Staffelpreis zu Aktionspreis, VRU 21.06.2016
if ( (parseFloat(produkt.MENGE) >= parseFloat(produkt.AKTIONSPREIS6)) && (parseFloat(produkt.AKTIONSPREIS3)/ if( (produkt.MENGE >= 6) && ( parseFloat(produkt.AKTIONSPREIS6) < parseFloat(produkt.PREIS) ) ) {
// produkt.PAKETPREIS = produkt.AKTIONSPREIS6;
// produkt.SPARPREIS = ( parseFloat(produkt.PREIS) - parseFloat(produkt.AKTIONSPREIS6));
// }
// if( (produkt.MENGE >= 12) && ( parseFloat(produkt.AKTIONSPREIS12) < parseFloat(produkt.PREIS) ) ) {
// produkt.PAKETPREIS = produkt.AKTIONSPREIS12;
// produkt.SPARPREIS = ( parseFloat(produkt.PREIS) - parseFloat(produkt.AKTIONSPREIS12));
// }
produkt.GESAMTPREIS = parseFloat(Preis * menge).toFixed(2);
// produkt.GESAMTPREIS = parseFloat(produkt.PAKETPREIS * produkt.MENGE).toFixed(2);
produkt.GESAMTSPARPREIS = produkt.SPARPREIS * produkt.MENGE;
produkt.MENGE=parseInt(produkt.MENGE);
if (redu==true){
// produkt.MENGE=produkt.MENGE*2
produkt.MENGE=produkt.MENGE*produkt.BOMRABATT
}
// create new entry
POSITION = {
MATNR:produkt.MATNR,
MAKTX:produkt.MAKTX,
MENGE:produkt.MENGE,
MAXMENGE:produkt.MAXMENGE,
KATTEXTKURZ:produkt.KATTEXTKURZ,
IMG_BIG:produkt.IMG_BIG.replace(locStatic,""),
IMG_THUMB:produkt.IMG_THUMB.replace(locStatic,""),
BACK_PREIS: produkt.PREIS,
PREIS:Preis,
GESAMTPREIS:parseFloat(produkt.GESAMTPREIS).toFixed(2),
STAFFELPREIS:produkt.STAFFELPREIS,
AKTIONSPREIS:produkt.AKTIONSPREIS,
WAEHRUNG:produkt.WAEHRUNG,
PAKETPREIS:produkt.PAKETPREIS,
SPARPREIS:produkt.SPARPREIS,
GESAMTSPARPREIS:produkt.GESAMTSPARPREIS,
AKTIONSPREIS3 : produkt.AKTIONSPREIS3,
AKTIONSPREIS6 : produkt.AKTIONSPREIS6,
STAFFELPREIS3 : produkt.STAFFELPREIS3,
STAFFELPREIS6 : produkt.STAFFELPREIS6,
BOMRABATT: produkt.BOMRABATT,
VERPACKUNGSEINHEIT:produkt.VERPACKUNGSEINHEIT
};
data.WK_POSITIONEN[data.WK_POSITIONEN.length] = POSITION;
} else {
//...Ansonsten Menge aendern
if(updateByField){
POSITION.MENGE=parseInt(POSITION.MENGE) + parseInt(produkt.MENGE)
if (redu!=undefined && redu ==true){
POSITION.MENGE = POSITION.MENGE/2 + parseInt(produkt.MENGE)/2
}
var Preis = parseFloat(produkt.PREIS).toFixed(2);
if (produkt.STAFFELPREIS3!=""){
if( (parseFloat(POSITION.MENGE) >= parseFloat(produkt.STAFFELPREIS6)) && ( parseFloat(produkt.STAFFELPREIS3) < parseFloat(produkt.PREIS) ) ) {
POSITION.PAKETPREIS = produkt.STAFFELPREIS3;
POSITION.SPARPREIS = ( parseFloat(produkt.PREIS) - parseFloat(produkt.STAFFELPREIS3));
var Preis = produkt.STAFFELPREIS3
}
}
if (produkt.AKTIONSPREIS3!=""){
if( (parseFloat(POSITION.MENGE) >= parseFloat(produkt.AKTIONSPREIS6)) && ( parseFloat(produkt.AKTIONSPREIS3) < parseFloat(produkt.PREIS) ) ) {
POSITION.PAKETPREIS = produkt.AKTIONSPREIS3;
POSITION.SPARPREIS = ( parseFloat(produkt.PREIS) - parseFloat(produkt.AKTIONSPREIS3));
var Preis = produkt.AKTIONSPREIS3
}
}
if (parseFloat(produkt.AKTIONSPREIS)< parseFloat(produkt.PREIS) && produkt.AKTIONSPREIS!=""){
var Preis = parseFloat(produkt.AKTIONSPREIS).toFixed(2);
}
// Vergleich Staffelpreis zu Aktionspreis, VRU 21.06.2016
if ( (parseFloat(POSITION.MENGE) >= parseFloat(produkt.AKTIONSPREIS6)) && (parseFloat(produkt.AKTIONSPREIS3)/ if( (POSITION.MENGE >= 6) && ( parseFloat(POSITION.AKTIONSPREIS6) < parseFloat(POSITION.PREIS) ) ) {
// POSITION.PAKETPREIS = POSITION.AKTIONSPREIS6;
// POSITION.SPARPREIS = ( parseFloat(POSITION.PREIS) - parseFloat(POSITION.AKTIONSPREIS6));
// }
// if( (POSITION.MENGE >= 12) && ( parseFloat(POSITION.AKTIONSPREIS12) < parseFloat(POSITION.PREIS) ) ) {
// POSITION.PAKETPREIS = POSITION.AKTIONSPREIS12;
// POSITION.SPARPREIS = ( parseFloat(POSITION.PREIS) - parseFloat(POSITION.AKTIONSPREIS12));
// }
POSITION.GESAMTPREIS = parseFloat(POSITION.MENGE*POSITION.PAKETPREIS).toFixed(2);
POSITION.GESAMTSPARPREIS = POSITION.MENGE*POSITION.SPARPREIS;
POSITION.STAFFELPREIS = produkt.STAFFELPREIS;
POSITION.AKTIONSPREIS = produkt.AKTIONSPREIS;
POSITION.WAEHRUNG = produkt.WAEHRUNG;
if (redu!=undefined && redu ==true){
// POSITION.MENGE = POSITION.MENGE*2
POSITION.MENGE = POSITION.MENGE*POSITION.BOMRABATT
}
}else{
}
POSITION.AKTIONSPREIS3 = produkt.AKTIONSPREIS3
POSITION.AKTIONSPREIS6 = produkt.AKTIONSPREIS6
POSITION.STAFFELPREIS3 = produkt.STAFFELPREIS3
POSITION.STAFFELPREIS6 = produkt.STAFFELPREIS6
POSITION.BOMRABATT = produkt.BOMRABATT
POSITION.BACK_PREIS= produkt.PREIS,
POSITION.PREIS = Preis;
POSITION.GESAMTPREIS= toFixed(POSITION.GESAMTPREIS,2);
POSITION.GESAMTSPARPREIS= toFixed(POSITION.GESAMTSPARPREIS,2);
POSITIONEN[POSITIONIndex] = POSITION;
data.WK_POSITIONEN = POSITIONEN;
}
// Gesamtpreis neu berechnen
data.GESAMTPREIS = 0;
data.GESAMTMENGE = 0;
data.ZWISCHENSUMME = 0;
data.GESAMTSPARPREIS = 0;
for (var xxy = 0 ; xxy < data.WK_POSITIONEN.length ; xxy ++) {
data.GESAMTPREIS += parseFloat(data.WK_POSITIONEN[xxy].GESAMTPREIS);
if (istmindestmenge (data.WK_POSITIONEN[xxy],"","call")==true && redu==true){
data.GESAMTMENGE += parseInt(data.WK_POSITIONEN[xxy].MENGE);
}else{
data.GESAMTMENGE += parseInt(data.WK_POSITIONEN[xxy].MENGE);
}
//hier Gesamtmenge wieder erhoehen
data.ZWISCHENSUMME += parseFloat(data.WK_POSITIONEN[xxy].GESAMTPREIS);
data.GESAMTSPARPREIS += parseFloat(data.WK_POSITIONEN[xxy].GESAMTSPARPREIS);
}
data.GESAMTPREIS = parseFloat(data.GESAMTPREIS).toFixed(2)
data.GESAMTSPARPREIS = parseFloat(data.GESAMTSPARPREIS).toFixed(2);
// data.INTERNETRABATT = parseFloat(menge) + parseFloat(menge)
// data.GESAMTPREIS= parseFloat(data.GESAMTPREIS).toFixed(2);
data.ENDPREIS=parseFloat(data.GESAMTPREIS).toFixed(2) - parseFloat(data.INTERNETRABATT).toFixed(2);
data.ZWISCHENSUMME= toFixed(data.ZWISCHENSUMME,2);
data.GESAMTSPARPREIS= toFixed(data.GESAMTSPARPREIS,2);
data.ERSATZLIEFERUNG = data.ERSATZLIEFERUNG;
data.LIEFERART=data.LIEFERART;
data.LIEFERDATUM = data.LIEFERDATUM;
data.KUNDENNACHRICHT=data.KUNDENNACHRICHT;
// if(parseFloat(oData.INTERNETRABATT)>0){
// //sap.ui.getCore().byId("app").getModel("warenkorb").setProperty("/INTERNETRABATT",parseFloat(oData.INTERNETRABATT).toFixed(2));
// sap.ui.getCore().byId("app").getModel("warenkorb").setProperty("/ENDPREIS",(parseFloat(sap.ui.getCore().byId("app").getModel("warenkorb").getProperty("/GESAMTPREIS")) - parseFloat(oData.INTERNETRABATT)).toFixed(2));
// }
// // Model updaten
model.setData(data,"warenkorb");
if($.cookie("cookieUser")!=undefined){
setBackendWK(sap.ui.getCore().byId("app").getModel("kategorie"),sap.ui.getCore().byId("app").getModel("userData").oData.KUNDENNR);
}else{
setCookieCart(model);
}
if (redu==true){
// menge=menge*2;
menge=menge*produkt.BOMRABATT;
}
showToastMessage(menge,produkt.MAKTX,produkt.MERKMAL.results);
//Speichern des Warenkorbes in einen Cookie
simulateOrder();
spinner.stop();
},function(oError){
spinner.stop();
messageToast(app.getModel("i18n").getProperty("keineWare"));
});
};
Marigold adds: “I have no words for this. Make something up. I don’t care.”
It isn’t about what this code does, as much as the sheer mass of it, the weight of 350+ lines of code in one gigantic method which seems to do everything, makes me want to do nothing but eat a box of “einen Cookies” in one sitting.
https://thedailywtf.com/articles/nothing-direct-about-directaddcartentry
|
Метки: CodeSOD |
CodeSOD: Br"ut"al Gl"obs |
Noam and a few friends decided it was time for them to launch their own product. They were young, optimistic about their career, and had some opinions about the best way to handle some basic network monitoring and scanning tasks. So they iterated on the idea a few times, until one day the program just started hanging. At first, Noam thought it was just a hang, but after walking away from the machine for a few minutes in frustration, he discovered that it was just running really slow.
After a little investigation, he tracked down the problem to the function responsible for checking if an IP matched a filter. That filter could contain globs, which made things a bit tricky, but his partner had some ideas about how best to handle them.
def ip_check(ip, rule):
ret_value = False # Return Value
if ip == rule['host']: # Compare against rule
ret_value = True
elif '*' in rule['host']: # Handle wildcards
mask = rule['host'].split('.')
length = mask.count('*')
final = []
for subset in itertools.permutations(range(256), length):
final.append(list(subset))
for item in final:
address = rule['host'].split('.')
for index in range(length):
address[address.index('*')] = str(item[index])
address = '.'.join(address)
if address == ip:
ret_value = True
return ret_value
This code takes a long way around.
We start with for subset in itertools.permutations(range(256), length):. itertools.permutations does exactly what you think- in this case, it creates every possible permutation of the numbers in the range 0–255, taken length at a time- where length is the number of wildcards. So, for example, 10.1.*.*, is a mere 65,280 entries. *.*.*.*, which is what Noam was doing when testing, is a lot more. 4,195,023,360 entries, to be exact.
Then we iterate across every possible combination to put them into the final list. The permutations method is smart, it lazily evaluates the permutations, calculating the next one when you need it. As you can see, Python does allow you to iterate across it. So we don’t need the final variable at all, we could have simply done for item in itertols.permutations(…) and that would have been fine. Well, not fine, none of this is fine.
So, we populate a list with every possible permutation, then we iterate across every permutation. We incorrectly slam the permuted values into the test string, and if that test string matches our IP, we set the ret_value to True. And then we keep looping. This block doesn’t even take the simplest optimization of simply quitting when it finds what it’s looking for.
Noam rewrote this function, replacing it with a much simpler 3-line function using built-in methods. Then Noam went on to have a long conversation with his team about how something like this happened.
|
Метки: CodeSOD |
The Enterprise Backup Batch |
If a piece of software is described in any way, shape or form as being "enterprise", it's a safe bet that you don't actually want to use it. As a general rule, "enterprise" software packages mix the Inner-Platform Effect with trying to be all things to all customers, with thousands upon thousands of lines of legacy code that can't be touched because at least one customer depends on those quirks. There doesn't tend to be much competition in the "enterprise" space, so none of the vendors actually put any thought into making their products good. That's what salesbeasts and lawyers are for.
Kristoph M supports a deployment of Initech's data warehouse system. Since this system is a mix of stored procedures and SSIS packages, Kristoph can actually read a good portion of the code which makes the product work. They just choose not to. And that's usually a good choice.
But one day, while debugging, Kristoph decided that they needed a simple answer to a simple question: "For a SQLAgent Job, how do you create a backup of the database with the day appended to the filename?"
SQLAgent is SQL Server's scheduling system, used for triggering tasks. SSIS is SQL Server's "drag and drop" dataflow tool, designed to let users draw data pipelines to handle extract-transform-load tasks.
In this case, the SQLAgent job's first step was to launch an SSIS package. Already, we're in questionable territory. SSIS is, as stated, an ETL tool. Yes, you can use it to extract data, it's not really meant as a replacement for an actual database backup.
The good news is that this SSIS package doesn't actually do anything to backup the database. Instead, it contains a single task, and it isn't a data flow task, it's a "Visual Basic Script Task". Yes, SSIS lets you run a stripped down Visual Basic dialect in its context. What does this task do?
Public Sub Main()
'
' Add your code here
'
Dim sToday As Date = Now
Dim sDay As String = sToday.Day.ToString
If CInt(sDay) < 10 Then sDay = "0" & sDay
Dim sMonth As String = MonthName(Month(sToday), True)
Dim sYear As String = Year(sToday).ToString
Dim sPara1 As String = sDay '& sMonth & sYear
Dim sPath As String = "D:\Initech\DailyProcess\"
Using fso As StreamWriter = New StreamWriter(sPath & "runBackupBatch.bat")
fso.WriteLine(sPath & "DailyExtractBackup.bat " & sPara1)
fso.Close()
End Using
Dts.TaskResult = ScriptResults.Success
End Sub
This figures out the current day, and then writes out a runBackupBatch.bat file with contents like this:
D:\Initech\DailyProcess\DailyExtractBackup.bat 02
Once that step is completed, the SQLAgent job continues, and runs the runBackupBatch.bat, which in turn runs DailyExtractBackup.bat, which does this:
D:\Initech\DailyProcess\DailyExtractBackup.bat
@echo off
@echo Dumping DailyExtract database...
osql -E -Slocalhost -oD:\Initech\DailyProcess\DailyExtractDump.log -Q"backup database DailyExtract to DISK='D:\Initech\MSSQL\Backup\DailyExtractDump%1.bak' with INIT"
if errorlevel 1 goto dumperror
REM Check for SQL Errors
findstr "Msg" D:\Initech\DailyProcess\DailyExtractDump.log
if not errorlevel 1 goto dumperror
:OK
@echo All Done!!
exit 0
:dumperror
@echo Error dumping database.
exit 1
The osql call is about the first reasonable step in this process. That actually does the backup using SQL server's backup tools. Then again, the mechanism to see if there were any errors in the logfile is troubling. findstr sets the errorlevel to 1 if Msg is not found in the log file. So, if Msg is not not found in the logfile, we'll go to dumperror.
After reading through this process, Kristoph decide it was best to take a step outside, get some air, and stop thinking about the other horrible things that might be lurking in Initech's data warehouse product.
[Advertisement]
Forget logs. Next time you're struggling to replicate error, crash and performance issues in your apps - Think Raygun! Installs in minutes. Learn more.
https://thedailywtf.com/articles/the-enterprise-backup-batch
|
Метки: Feature Articles |
Error'd: Errors Don't Always Ad up |
"You know, I'm thinking that The guys working on AT&T's DIRECTV service must have not done well with fractions in school," Andrew T. writes.
"Come on, DNS Exit, you shouldn't objectify your users!" writes Lance G.
Tom G. wrote, "I guess if I wanted my actual name to appear here I should have signed in with my Microsoft account and not my 20 year old Skype account."
Michael P. wrote, "Tripp Lite's site is pretty smart to detect a mismatched password before I've even registered on their site!"
"I was using my Ubuntu laptop, when my app crashed," writes Will B., "I didn't realize that my OS needed to crash my app whenever it was ready."
"Well, it looks like I'm about to enter the Matrix. So, do I click the gray pill or the white pill?" writes Jim S.
[Advertisement]
Forget logs. Next time you're struggling to replicate error, crash and performance issues in your apps - Think Raygun! Installs in minutes. Learn more.
|
Метки: Error'd |
CodeSOD: Null Error Handling |
Oliver works for a very large company. Just recently, someone decided that it was time to try out those “newfangled REST services.”
Since this was “new”, at least within the confines of the organization, that meant there were a lot more eyes on the project and a more thorough than average code review process. That’s how Oliver found this.
@SuppressWarning("null")
public void uploadData(Payload payload) {
// preparation of payload and httpClient
//...
@NonNull
HttpResponse response = null;
response = httpClient.execute(request);
if (response.getStatus() != 200) {
throw new RuntimeException(
String.format(
"Http request failed with status %s",
response.getStatus()
);
}
}
The purpose of this code is to upload a JSON-wrapped payload to one of the restful endpoints.
Let’s start with the error handling. Requiring a 200 status code is probably not a great idea. Hope the server side doesn’t say, “Oh, this creates a new resource, I had better return a 201.” And you might be thinking to yourself, “Wait, shouldn’t any reasonable HTTP client raise an exception if the status code isn’t a success?” You’d be right, and that is what this httpClient object does. So if the status isn’t a success code, the error throw statement will never execute, meaning we only error if there isn't one.
The HttpResponse variable is annotated with a @NonNull annotation, which means at compile time, any attempt to set the variable to null should trigger an error. Which, you’ll note, is exactly what happens on the same line.
It’s okay, though, as the dependency setup in the build file was completely wrong, and they never loaded the library which enforced those annotations in the first place. So @NonNull was just for decoration, and had absolutely no function anyway.
|
Метки: CodeSOD |
CodeSOD: Structured Searching |
It’s hard to do any non-trivial programming in C without having to use a struct. Structs are great! A single variable holds access to multiple pieces of data, and all the nasty details of how they’re laid out in memory are handled by the compiler.
In more modern OO languages, we take that kind of thing for granted. We’re so abstracted from the details of how memory is laid out it’s easy to forget how tricky and difficult actually managing that kind of memory layout is.
Of course, if you’re Jean-Yves R’s co-worker, letting structs manage your memory layout is beginner mode stuff.
Jean-Yves was trying to understand why a bunch of structs were taking up huge amounts of memory, relative to how much they should take. Every bit of memory mattered, as this was an embedded application. Already, these structs weren’t actually stored in RAM, but in the flash memory on the device. They served as a database- when a request came in over Modbus or CAN or I2C, the ID on the request would be used to look up the struct containing metadata for handling that request. It was complex software, so there were a lot of these structs taking up flash memory.
It didn’t take long to see that the structs were defined with padding to ensure every field fell on a multiple of 32-bits, which meant there were huge gaps in every struct. Why? Well, this is an example of how they’d search the database:
/* These lines are actually in an included header */
#define DAT_calcOffset(address,offset) (address += offset)
#define DAT_MODBUS_ID_OFFSET 0x00000002 /* (32 bits pointer) */
/*
* Database search code
*/
/*
* Setting up start adress at beginning of flash zone + an
* offset corresponding to the member of struct
*/
DAT_calcOffset(pu32SearchBaseAddress,DAT_MODBUS_ID_OFFSET);
/* Return : Status */
if(u16ModBusID >= 0x8000)
{
u16ModBusID -= 0x8000;
bReturnStatus = TRUE;
}
/* Increment until we find the correct ID */
while((*pu32SearchBaseAddress != u16ModBusID) && (u16Index < u16NbOfData))
{
pu32SearchBaseAddress += (sizeof(DAT_typDataArray)/4);
u16Index++;
}
if(u16Index == u16NbOfData)
{
if(penFlagReturn)
*penFlagReturn = (DAT_typFlag)DAT_enCFlagErrOF;
xSemaphoreGive(DAT_tMutexDatabase);
return DAT_tNullStr;
}
pu32SearchBaseAddress is a pointer to a struct in the flash memory, at least until the DAT_calcOffset macro does a little pointer arithmetic to point it at a field within the struct- specifically the modbus message ID. Then, in the while loop, we keep incrementing that pointer based on sizeof(DAT_typDataArray)/4- which is the size of our struct.
You might be wondering, why not do something sane and readable, like pu32SearchBaseAddress->modbus_id? Well, obviously this is “optimized”.
Like all “optimizations”, this one is a tradeoff. In this case, the memory layout of the structs is now fixed, and the structs cannot ever evolve in the future without completely breaking this code. It’s also not portable, due to memory sizes.
On the flip side, this also offers many benefits. The code is cryptic and unreadable, which helps ensure job security. The fact that each type of message- modbus, CANbus, I2C, etc.- has its own database means that this code has to be written for each one of those protocols, ensuring that the developer always has lots of code to copy/paste between those datasets with minor changes to constants and variable names. This keeps their lines-of-code count up in the source control metrics.
It probably didn’t do anything for performance of course, as pretty much any C compiler is going to compile the -> operator into a static memory offset anyway, thus making this “optimization” pretty useless for performance.
|
Метки: CodeSOD |
Process by Management |
Alice's team was thirty developers, taking up most of the floor of a nondescript office building in yet another office park. Their team was a contractor-to-a-contractor for a branch of the US military, which meant a variety of things. First, bringing a thumb drive into the office was a firing offense. Second, they were used to a certain level of bureaucracy. You couldn't change a line of code unless you had four different documents confirming the change was necessary and was authorized, and actually deploying a change was a milestone event with code freezes and expected extra hours.
Despite all this, the thirty person team had built a great working relationship. They had made their process as efficient as they could, and their PM, Doug, understood the work well enough to keep things streamlined. In fact, Doug did such a good job that Doug got promoted. Enter Millie, his replacement.
Millie had done a stint in the Air Force and then went back to school for her MBA. She had bounced around a few different companies, and had managed to turn every job change into a small promotion. This was Millie's first time overseeing a pool of software developers, but she had an MBA. Management was management, and there was no reason she had to understand what developers did, so long as she understood the key performance indicators (KPI).
Like the quantity of defects. That was a great KPI, because it was measurable, had a clear negative impact, and it could be mitigated. Mitigated with a process.
After a few weeks of getting her bearings, Millie called a meeting. "Alright, everyone, I've been observing a little bit of how we work, and I think there may be some communication and organization issues, so I wanted to address that. I've looked at our current workflow, and I've made a few small changes that I wanted to review."
On one side of the white board, she drew a bubble labeled "In Production". "This is where we want our code to be, right? Working, quality-controlled code, in production, with no defects." On the opposite side of the board, she added a bubble for "PCCB Ticket." "And any code change starts with one of these- the Product Change Control Board reviews an open ticket. They'll then turn that ticket into a Functional Requirement Document." Millie added another bubble for that.
Alice had some questions already, but not quite about the inputs or outputs.
"Great, okay, so… we need to iterate on the FRD, and once the PCCB signs off we'll convert that to a System Requirement Document. Either a PM or a SME will decompose the SRD into one or more Work Packages."
Millie continued scribbling furiously as she explained exactly what a work package was, as this wasn't currently a term in use at their organization. Her explanation wasn't terribly clear, as Millie explained it as the set of steps required to implement a single feature, but a Functional Requirement was a feature, so how was the Work Package (WP) any different than the FRD?
"Please, hold your questions until the end, we have a lot to get through."
Finally, once the Work Package was analyzed, you could create a "Ticket Lifecycle Document", a new document which would hold all information about all of the efforts put towards the PCCB ticket. Which meante the TLD contained all the WPs, which raised questions about the point of adding work packages. From the TLD to a new PCCB ticket- a "Ready" ticket, then finally those requirements could go onto a Release backlog and a release management plan could be created.
"Finally," Milile explained, "we're ready to write code." In the center of the board, she added a single bubble: "Code".
And on and on the meeting went. The diagram grew. Lines kept getting added. Bubbles got inserted between existing bubbles. Arrows pointed to labels, or to bubbles, or maybe to arrows? By the end of Millie's meeting, it looked something like this.
"There, that lays out the correct pattern for getting our software to production, with a feedback loop that prevents defects. Any questions?"
There weren't any questions at the meeting, no. But boy, were there questions. Loads of questions. Questions like, "What font should I use on my resume?" and "is it time to stop listing my VBA experience on my resume?"
Over the next few months, under Millie's leadership, 17 developers from the 30 person team left the company, Alice among them. Every once in awhile, Alice checks the job listings for that company, to see if those developer positions have been filled. They're still hiring for software developers. Unfortunately, Alice hasn't seen any openings for a PM, so Millie is probably still there.
|
Метки: Feature Articles |
CodeSOD: The Bogus Animation |
Animations have become such an omnipresent part of our UI designs anymore that we tend to only notice them when they're bad.
Ben is working on an iOS application which appeared to have a "bad" animation. In this case, it's bad because it's slow. How slow? Well, they have a table view with ten items in it, and the items should quickly tween to their new state- position, text, colors all could change in this process. And it was taking four seconds.
Four seconds to update ten items is a lot. Now, their application does have a lot of animations, and the first suspicion was that there was some ugly interaction between animations that was causing it to take a long time. But upon digging in, Ben discovered it wasn't the animations at all.
- (NSArray *)_combineTitles:(NSArray *)oldTitles with:(NSArray *)newTitles
{
NSMutableSet *mergedSet = [NSMutableSet setWithArray:oldTitles];
[mergedSet unionSet:[NSSet setWithArray:newTitles]];
NSMutableArray *combinedTitles = [mergedSet.allObjects mutableCopy];
// TODO - this is a terrible method!
// We should be able to properly determine the/a correct order of combinedTitles, not
// simply trying random orders until we find the right one
// Note unless the assumption stated in _reloadTableDataAnimatedAdvanced is met, this could loop forever
// A better way to do this would be to consider the diff between oldTitles and newTitles
// E.g. the diff for ABC -> CDE has insertions at 1, 2 and deletions at 0, 1 and therefore the diff is based
// on the deletions being performed first - instead we want to construct the intermediate ABCDE which has insertions at 3, 4
while (YES)
{
DiffUpdate *diffUpdate1 = [DifferUtils getDiffWithOldData:oldTitles newData:combinedTitles];
DiffUpdate *diffUpdate2 = [DifferUtils getDiffWithOldData:combinedTitles newData:newTitles];
if (diffUpdate1.moves.count == 0 && diffUpdate2.moves.count == 0)
{
return [NSArray arrayWithArray:combinedTitles];
}
// Fisher-Yates shuffle
// https://stackoverflow.com/a/33840745
for (NSUInteger i = combinedTitles.count; i > 1; i--)
{
[combinedTitles exchangeObjectAtIndex:i - 1 withObjectAtIndex:arc4random_uniform((u_int32_t)i)];
}
}
return nil;
}
Obj-C is a bit odd to read, but this defines a method that combines a list of old item titles with a list of new item titles. The resulting output should be sorted based on the insertion order of the items. And how do we do this in this code?
Well, we check if the output list is in the right order by taking a diff between the output and the two inputs. If it's in the right order, great, return our results. If it's not… we do a Fisher-Yates shuffle, which is to say, this is an actual bogosort in the wild.
At least the documentation is useful. Not only does it include the accurate This is a terrible method, but goes on to lay out exactly what a better method might look like.
That's not the best part of the comment, though. It's this one:
// Note unless the assumption stated in _reloadTableDataAnimatedAdvanced is met, this could loop forever
[Advertisement]
Forget logs. Next time you're struggling to replicate error, crash and performance issues in your apps - Think Raygun! Installs in minutes. Learn more.
|
Метки: CodeSOD |
Classic WTF: Working Around, Over and Through the Process |
It's still a holiday weekend in the US; after playing with fireworks yesterday, most of us have to spend today trying to find the fingers we lost. There are no fireworks in this classic story, but there may be some karma… Original --Remy
When Kevin landed a job at Townbank in the late 1980s, he came face-to-face with the same thing that thousands of newly minted developers had encountered before and since – there is more to being a corporate programmer than just writing code – there’s the process.
Second only, perhaps, to the strict rules commanded by the world’s religions, the process keeps the code consistent. Glory to the process – praised be the process - the process is good, the process should always be followed, and above all, the process is good for you!
For nearly everybody, the process isn’t all that bad. It just takes some getting used to. Fill out a form, get a sign off, file the test plan, write a build document - all in a day's work. However, as Kevin would soon find out, at Townbank, there were some processes both veterans and new grads alike couldn’t adjust to.
Kevin’s first assignment was to work within a group involved with Townbank’s IT department’s largest project to date - their huge migration from their aging mainframe to a row of shiny new VAX systems. On paper, the process looked good - consultants met with the business to identify which systems would be migrated, specs would be written, developers would be assigned, QA would confirm that the new system worked like the old one and the code would be promoted into production.
When the project managers first set up the process, the expectation was that the extra steps would only ever be responsible for a small fraction of the total cost or amount of time necessary for implementing a feature, and at first, this was the case. However, as the project continued and more environments were finally brought online, Kevin and his fellow developers knew to add an extra bit of padding to their estimates. Officially, the developers called it many names – systems integration testing, server configuration, environment compatibility testing, but only in hushed voices could it be called out by its true name: “The Shiva Factor”
Now, it wasn’t that Shiva was an incompetent or inexperienced system administrator – not by a long shot. In fact, at Townbank, when the decision was made to migrate away from the mainframe to VAX, Shiva’s name was at the top of a very short list of individuals who should manage the infrastructure migration and Shiva took his responsibility very seriously and enacted several of his own policies to address what he felt to be loopholes in the process. For example, every morning, before any developers, analysts, and QA staff could sign-in to an environment, they first had to literally sign-in on a clipboard on Shiva’s desk, to confirm their physical presence. Also, feeling that the process did not track developer actions to a high enough granularity, Shiva arranged source control security so that every code check in and promote between environments required a write up with 2 signatures and had to be performed using his user id. At his terminal.
On quiet days, a quick change could be turned around in one day, however, the quiet days were often holidays or weekends. Frustrated developers took their case to upper management arguing that the policies were hindering progress and seemed to be completely useless. In response, management shrugged – Shiva made his case at the beginning of the project – the environments secure and free from cross-contamination by other instances and developer incompetence because, after all, the VAX servers were still very new and even many of the senior developers were not entirely up to speed.
The masses grumbled and cursed under their breath, but rather than rising up and overthrowing Shiva and ending his iron-fisted reign, everybody just kind of sucked it up and moved forward. Albeit annoying, the process continued in spite of Shiva’s efforts, however there was one situation that Shiva seemingly neglected – what if he was unavailable?
Though Kevin’s terminal showed that he was on a clone of the Production environment, his tell-tale customer names “Nosmo King” and “Joe Blow” made him realize the he had made a grave error – the application was connecting to the Development environment’s database by mistake and it was to be tested by the QA team later that afternoon. Ordinarily, fixing this was a piece of cake - make a few changes to the config file in the Development environment and re-promote, however, as fate would have it, Shiva was in a day-long meeting and would not be available until the next day.
Hoping that maybe Shiva left his meeting early, Kevin stopped by Shiva’s desk but was met with only his empty chair, however, a detail about Shiva’s keyboard stood out. The letters A, S, V, H, and I all had their letters worn away. Kevin knew that Shiva was drunk with power, but was he so narcissistic so as to type his name in over and over? …or perhaps it was a hint. For fun, Kevin navigated to a command prompt and typed in “shiva” for both the username and password. Expecting Shiva to sneak up on him at any moment, Kevin pressed enter and was shocked and amazed to discover that he was now logged in.
This was amazing. This was huge. Kevin knew he had to find someone to share his discovery with, however, after tracking down and relaying his discovery with one of the gray beards who had mentored him earlier in his tenure, the reaction was not at all what Kevin expected.
As it turned out, Shiva’s username and password combination were a favorite Townbank “secret” that carried over from Shiva’s days as a mainframe admin.
“To keep Shiva from catching on,” the more senior developer explained, “we would play Shiva’s game once every other promotion.”
“Also, for future reference,” he continued,” if you want to avoid getting caught, and ruining it for everybody else, you might want to log in from your own terminal and NOT from his desk.”
[Advertisement]
Forget logs. Next time you're struggling to replicate error, crash and performance issues in your apps - Think Raygun! Installs in minutes. Learn more.
https://thedailywtf.com/articles/classic-wtf-working-around-over-and-through-the-process
|
Метки: Feature Articles |
Representative Line: Classic WTF: The Backup Snippet |
It's "Independence Day" here in the US, which is the day in which developers celebrate their independence from DBAs and switch everything over to NoSQL, no matter what the cost. Or something like that, the history is a little fuzzy. But it's a holiday here, so in honor of that, here's a related story. Original --Remy
Generally speaking, Andrew tries his best to avoid the DBA team. It's not just because database administrators tend to be a unique breed (his colleagues were certainly no exception), but because of the "things" that he'd heard about the team. The sort of "things" that keep developers up at night and make them regret not becoming an accountant.
One day, while debugging an issue with their monitoring scripts, Andrew had no choice but to check with Thom, a member of Team DBA. It turned out that one of DBA's had recently updated their database backup script, but Thom wasn't really sure who did it, why it was done, or what it looked like before. So, he just sent Andrew the entire backup script.
Following is a single line of code, line-wrapped by yours truly, that should give a fair idea of what the script was like.
file=$WORKSPACE/ewprd1_$DATECODE.dmp,$WORKSPACE/ewprd2_$DATECODE.dm p,$WORKSPACE/ewprd3_$DATECODE.dmp,$WORKSPACE/ewprd4_$DATECODE.dmp,$ WORKSPACE/ewprd5_$DATECODE.dmp,$WORKSPACE/ewprd6_$DATECODE.dmp,$WOR KSPACE/ewprd7_$DATECODE.dmp,$WORKSPACE/ewprd8_$DATECODE.dmp,$WORKSP ACE/ewprd9_$DATECODE.dmp,$WORKSPACE/ewprd10_$DATECODE.dmp,$WORKSPAC E/ewprd11_$DATECODE.dmp,$WORKSPACE/ewprd12_$DATECODE.dmp,$WORKSPACE /ewprd13_$DATECODE.dmp,$WORKSPACE/ewprd14_$DATECODE.dmp,$WORKSPACE/ ewprd15_$DATECODE.dmp,$WORKSPACE/ewprd16_$DATECODE.dmp,$WORKSPACE/e wprd17_$DATECODE.dmp,$WORKSPACE/ewprd18_$DATECODE.dmp,$WORKSPACE/ew prd19_$DATECODE.dmp,$WORKSPACE/ewprd20_$DATECODE.dmp,$WORKSPACE/ewp rd21_$DATECODE.dmp,$WORKSPACE/ewprd22_$DATECODE.dmp,$WORKSPACE/ewpr d23_$DATECODE.dmp,$WORKSPACE/ewprd24_$DATECODE.dmp,$WORKSPACE/ewprd 25_$DATECODE.dmp,$WORKSPACE/ewprd26_$DATECODE.dmp,$WORKSPACE/ewprd2 7_$DATECODE.dmp,$WORKSPACE/ewprd28_$DATECODE.dmp,$WORKSPACE/ewprd29 _$DATECODE.dmp,$WORKSPACE/ewprd30_$DATECODE.dmp,$WORKSPACE/ewprd31_ $DATECODE.dmp,$WORKSPACE/ewprd32_$DATECODE.dmp,$WORKSPACE/ewprd33_$ DATECODE.dmp,$WORKSPACE/ewprd34_$DATECODE.dmp,$WORKSPACE/ewprd35_$D ATECODE.dmp,$WORKSPACE/ewprd36_$DATECODE.dmp,$WORKSPACE/ewprd37_$DA TECODE.dmp,$WORKSPACE/ewprd38_$DATECODE.dmp,$WORKSPACE/ewprd39_$DAT ECODE.dmp,$WORKSPACE/ewprd40_$DATECODE.dmp,$WORKSPACE/ewprd41_$DATE CODE.dmp,$WORKSPACE/ewprd42_$DATECODE.dmp,$WORKSPACE/ewprd43_$DATEC ODE.dmp,$WORKSPACE/ewprd44_$DATECODE.dmp,$WORKSPACE/ewprd45_$DATECO DE.dmp,$WORKSPACE/ewprd46_$DATECODE.dmp,$WORKSPACE/ewprd47_$DATECOD E.dmp,$WORKSPACE/ewprd48_$DATECODE.dmp,$WORKSPACE/ewprd49_$DATECODE .dmp,$WORKSPACE/ewprd50_$DATECODE.dmp,$WORKSPACE/ewprd51_$DATECODE. dmp,$WORKSPACE/ewprd52_$DATECODE.dmp,$WORKSPACE/ewprd53_$DATECODE.d mp,$WORKSPACE/ewprd54_$DATECODE.dmp,$WORKSPACE/ewprd55_$DATECODE.dm p,$WORKSPACE/ewprd56_$DATECODE.dmp,$WORKSPACE/ewprd57_$DATECODE.dmp ,$WORKSPACE/ewprd58_$DATECODE.dmp,$WORKSPACE/ewprd59_$DATECODE.dmp, $WORKSPACE/ewprd60_$DATECODE.dmp,$WORKSPACE/ewprd61_$DATECODE.dmp,$ WORKSPACE/ewprd62_$DATECODE.dmp,$WORKSPACE/ewprd63_$DATECODE.dmp,$W ORKSPACE/ewprd64_$DATECODE.dmp,$WORKSPACE/ewprd65_$DATECODE.dmp,$WO RKSPACE/ewprd66_$DATECODE.dmp,$WORKSPACE/ewprd67_$DATECODE.dmp,$WOR KSPACE/ewprd68_$DATECODE.dmp,$WORKSPACE/ewprd69_$DATECODE.dmp,$WORK SPACE/ewprd70_$DATECODE.dmp,$WORKSPACE/ewprd71_$DATECODE.dmp,$WORKS PACE/ewprd72_$DATECODE.dmp,$WORKSPACE/ewprd73_$DATECODE.dmp,$WORKSP ACE/ewprd74_$DATECODE.dmp,$WORKSPACE/ewprd75_$DATECODE.dmp,$WORKSPA CE/ewprd76_$DATECODE.dmp,$WORKSPACE/ewprd77_$DATECODE.dmp,$WORKSPAC E/ewprd78_$DATECODE.dmp,$WORKSPACE/ewprd79_$DATECODE.dmp,$WORKSPACE /ewprd80_$DATECODE.dmp,$WORKSPACE/ewprd81_$DATECODE.dmp,$WORKSPACE/ ewprd82_$DATECODE.dmp,$WORKSPACE/ewprd83_$DATECODE.dmp,$WORKSPACE/e wprd84_$DATECODE.dmp,$WORKSPACE/ewprd85_$DATECODE.dmp,$WORKSPACE/ew prd86_$DATECODE.dmp,$WORKSPACE/ewprd87_$DATECODE.dmp,$WORKSPACE/ewp rd88_$DATECODE.dmp,$WORKSPACE/ewprd89_$DATECODE.dmp,$WORKSPACE/ewpr d90_$DATECODE.dmp,$WORKSPACE/ewprd91_$DATECODE.dmp,$WORKSPACE/ewprd 92_$DATECODE.dmp,$WORKSPACE/ewprd93_$DATECODE.dmp,$WORKSPACE/ewprd9 4_$DATECODE.dmp,$WORKSPACE/ewprd95_$DATECODE.dmp,$WORKSPACE/ewprd96 _$DATECODE.dmp,$WORKSPACE/ewprd97_$DATECODE.dmp,$WORKSPACE/ewprd98_ $DATECODE.dmp,$WORKSPACE/ewprd99_$DATECODE.dmp,$WORKSPACE/ewprd100_ $DATECODE.dmp,$WORKSPACE/ewprd101_$DATECODE.dmp,$WORKSPACE/ewprd102 _$DATECODE.dmp,$WORKSPACE/ewprd103_$DATECODE.dmp,$WORKSPACE/ewprd10 4_$DATECODE.dmp,$WORKSPACE/ewprd105_$DATECODE.dmp,$WORKSPACE/ewprd1 06_$DATECODE.dmp,$WORKSPACE/ewprd107_$DATECODE.dmp,$WORKSPACE/ewprd 108_$DATECODE.dmp,$WORKSPACE/ewprd109_$DATECODE.dmp,$WORKSPACE/ewpr d110_$DATECODE.dmp,$WORKSPACE/ewprd111_$DATECODE.dmp,$WORKSPACE/ewp rd112_$DATECODE.dmp,$WORKSPACE/ewprd113_$DATECODE.dmp,$WORKSPACE/ew prd114_$DATECODE.dmp,$WORKSPACE/ewprd115_$DATECODE.dmp,$WORKSPACE/e wprd116_$DATECODE.dmp,$WORKSAPCE/ewprd117_$DATECODE.dmp,$WORKSPACE/ ewprd118_$DATECODE.dmp
Andrew eventually found the problem, and offered a helpful tip to Thom for shortening up their script:
file="$WORKSPACE/ewprd1_$DATECODE.dmp";
for ((i=2;$i<119;i++)); do {
FILE="$FILE,$WORKSPACE/ewprd$i_$DATECODE.dmp";
}; done
It accomplished the exact same thing in four little lines. Thom passed on the suggestion, however, perhaps because it would have increased the line count... or, most likely, to keep the developers out.
[Advertisement]
Forget logs. Next time you're struggling to replicate error, crash and performance issues in your apps - Think Raygun! Installs in minutes. Learn more.
https://thedailywtf.com/articles/classic-wtf-the-backup-snippet
|
Метки: Representative Line |
CodeSOD: Answer the Questions on this Test |
Jake works in Python. Python is a very flexible language, and Jake has some co-workers who like to exploit it as much as possible.
Specifically, they’re notorious for taking advantage of how any symbol can be redefined, as needed. Is int a built-in function? Yes. But what if you want to have a variable called int? No problem! Just go ahead and do it. Python won’t mind if you do int = int(5).
Sure, any linter will complain about the possible confusion, but do you really think this developer checks the linter output?
Now, this particular block of code happens to be a test, which Jake provided because it’s one of the more head-scratching versions of this pattern, but it’s representative of this approach.
def test_parameters(self, name, value, float = True):
fcb = read_parameters(SERVER_ADDRESS)
type = ''
if valid_key():
type = 'float'
if type == 'float':
self.assertTrue(float)
else:
self.assertFalse(float)
if float:
self.assertEqual(global_name, name)
default = fcb.get_default(name)
self.assertEqual(default, value)
range = fcb.get_range(name)
...
type, float and range are all built-in types in Python. Using variables with that name are not a great choice, but honestly, that’s little stuff compared to the general weirdness of this test.
First, we have the pattern where we set a variable and then immediately check its value. It’s probably a case where they indented incorrectly, but honestly, it’s a little hard to be sure.
global_name is one of the rare things that has a vaguely accurate name, as it’s actually a global variable. In a test. That’s definitely a bad idea.
But the real problem, and the real WTF here, is: what is this test about? Are we checking whether or not parameters are properly assigned their type? Are we checking that the name matches up with a global variable? How is that global variable getting set? Does it tie into the mysterious no-parameter valid_key method? If this is what one test looks like, what does the rest of the code look like?
There are a lot of questions that this block opens up, but the real question is: do I want the answer to any of those questions?
Probably not.
[Advertisement]
Forget logs. Next time you're struggling to replicate error, crash and performance issues in your apps - Think Raygun! Installs in minutes. Learn more.
https://thedailywtf.com/articles/answer-the-questions-on-this-test
|
Метки: CodeSOD |
CodeSOD: The Wizard of Speed and Time |
Christopher started a new job as a “full-stack” developer. Unfortunately, most of the developers are still on the “why do you need anything other than jQuery” school of front-end development, despite efforts to transition their UIs to Vue.
This meant that Christopher didn’t do much backend, and ended up being the mid-level front-end dev, in practice if not in job title.
One of the Vue components was a “Wizard” style component, which was designed to be highly configurable. You supply a JSON description of the Wizard process, and it would generate the UI to navigate you screen-by-screen. Since Christopher was new at the organization, he wanted to understand how the Wizard worked, so he started poking at the code.
He got as far as the stepBack function before deciding he needed to rewrite it from scratch. Christopher assumed that stepBack could be as simple as popping the last element off the array of previous steps, and then update what’s currently displayed. That, however, isn’t what it did at all.
stepBack () {
let pastItems = []
for (var i in this.pastStepsIds) {
if (pastItems.length === 0) {
pastItems.push(this.pastStepsIds[i])
}
for (var j in pastItems) {
if (pastItems[j] === this.pastStepsIds[i]) {
continue
}
pastItems.push(this.pastStepsIds[i])
}
}
if (pastItems.length) {
this.showAnswer = false
this.turnOffSteps(this.stepAtual)
this.currentStep = this.changeTextStep(this.getStepById(pastItems[pastItems.length - 1]))
this.turnOnStep(true)
}
}
Step IDs were named things like "welcome", "more-info", and "collect-user-info". pastStepsIds would contain an array of all of those.
What this code does is take the pastStepsIds and copies it into a local pastItems array. Except it’s not a straightforward copy, because of the inner for loop, which examines each item in the local array, and if the current item in the pastStepsIds array is equal to that item, we skip back to the beginning of the inner for loop.
The result is that this doesn’t copy the array, but exponentiates it. If the pastStepsIds looked something like ["welcome", "more-info", "collect-user-info", "confirm"], the loops would create: ["welcome", "more-info", "collect-user-info", "collect-user-info", "confirm", "confirm", "confirm"].
If that array has more than zero elements in it, we’ll return to the last step in the pastItems array.
Christopher spent a good bit of time thinking, “WHYYYYYYY?”. After some consideration, he decided that he didn’t deserve this code, and re-wrote it. In one day, he reimplemented the Wizard component, which probably saved him months of effort trying to fight with it in the future.
https://thedailywtf.com/articles/the-wizard-of-speed-and-time
|
Метки: CodeSOD |
Coded Smorgasbord: Classic WTF: Code Comedians |
Everybody's a comedian, until the joke's on them. We close out this week of classics with some code and comments by people who were being funny. We'll return to our regularly scheduled programming next week! Original. --Remy
When it comes to bad code, everybody thinks they’re a comedian. Heck, look at us! Stupid programmer jokes are a game everyone can play, though, so let’s enjoy an evening at the Improv with some code comedians.
Brian sends us this enum, which I’m sure was very funny back in 2007.
Public Enum TouchMessageBoxResponse [Ok] [Cancel] [Yes] [No] [DontTazeMeBro] End Enum
Brian claims that [DontTazeMeBro] is never returned, but if it were, I’m certain it would cause the application to Rickroll the user. Or taze them, I suppose.
Sometimes, a developer just has to puzzle over the application logic. Jeremy’s predecessor had some questions about this block of code:
public function Sanitize($string){ return null; #wtf is this doing here. That's a hell of a sanitization lol. }
Not all jokes in code are intentional. Tony sends us this code block, from Polish developers. He assures us that this is an innocent misuse of English.
//Don't expect much work to get done after this event is triggered CometWorker.OnFirstJoint += new FirstJointObjects(CometWorker_OnFirstJoint)
Hopefully that’s not the same Tony that worked in Gavin’s office, writing Excel macros.
On Error GoTo holysheet '… snip holysheet: MsgBox "Enter a sheet name Tony", vbOkOnly, "You sheethead, you bungled."
And finally, some developers make sure the joke is on those that come after them. David found this.
'Saw a lot of instances where the same box_num would get used elsewhere 'which caused this screen to show more units than were actually in this batch 'This is probably a serious issue 'I'll let you guys discover it after I'm gone. ;D 'Love, 'Martin strSql = "SELECT …"
|
Метки: Coded Smorgasbord |
Error'd: Not That Kind of Chicken |
"I probably wouldn't pick that picture, but technically, Google isn't wrong," wrote Dan K.
Daniel writes, "Yeah Amazon, that's right...I keep all the good stuff in my 'null' album."
"I was a little worried that Microsoft's new Edge browser flagged our company website as potentially malicious, but then I saw this," wrote Nigel.
"SO... Burger King wants me to wait ~10 days between orders? Maybe it's for the best?" writes Pascal.
"Oh, thank goodness...I thought that I was going to have to make a decision sometime this year," writes Geoff O.
Daniel writes, "My Dell laptop sure has a positive attitude when trying to install updates using its own management software."
|
Метки: Error'd |
CodeSOD: Classic WTF: The not-so-efficient StringBuilder |
As our short summer break continues, this one is from waaaaay back, but it's yet another example of how seemingly simple tasks and seemingly simple data-types can be too complicated sometimes. Original--Remy
The .NET developers out there have likely heard that using a StringBuilder is a much better practice than string concatenation. Something about strings being immutable and creating new strings in memory for every concatenation. But, I'm not sure that this (as found by Andrey Shchekin) is what they had in mind ...
public override string getClassVersion() {
return
new StringBuffer().append(
new StringBuffer().append(
new StringBuffer().append(
new StringBuffer().append(
new StringBuffer().append(
new StringBuffer().append(
new StringBuffer().append(
new StringBuffer().append(
new StringBuffer().append("V0.01")
.append(", native: ibfs32.dll(").ToString())
.append(DotNetAdapter.getToken(this.mainVersionBuffer.ToString(), 2)).ToString())
.append(") [type").ToString())
.append(this.portType).ToString())
.append(":").ToString())
.append(DotNetAdapter.getToken(this.typeVersionBuffer.ToString(), 0xff)).ToString())
.append("](").ToString())
.append(DotNetAdapter.getToken(this.typeVersionBuffer.ToString(), 2)).ToString())
.append(")").ToString();
}
Note, that it is J#, StringBuffer and StringBuilder are the same thing.
https://thedailywtf.com/articles/classic-wtf-the-not-so-efficient-stringbuilder
|
Метки: CodeSOD |
CodeSOD: Classic WTF: Uncovering Nothing |
As our little summer break continues, we have a tale about Remi (no relation) and a missing stored procedure. Original --Remy
Remi works on one of his country's largest Internet Service Providers, and has the fortune to be on an elite team that focuses on agile development. Or misfortune, depending on how you look at it: at his company, "agile development" actually means "we need that in two weeks".
One of Remi's first assignments was to fix an "emergency" on one of the ATM Addressing systems. Apparently, the application was coming up with incorrect routing data. After a solid day-and-a-half of digging through Visual Basic code that called SQL Server stored procedures which called VB-based COM objects which called more stored procs, Remi found a weird table ("Cal_ATM") that was referenced from an externally-linked database, and the data in that table was completely out of date.
Wondering how that table was populated, Remi searched high-and-low through the VB and stored proc code, but found nothing. Thinking it might be directly updated by some batch job, he looked through the job lists, and that's where he found it: a step buried in a job that called "EXECUTE PUpd_Cal_ATM". As it turned out, the step had been erroring-out for nearly a month-and-a-half with "Could not find stored procedure PUpd_Cal_ATM". No one knew about it, of course, because the failure notification was also not working.
The timing of the failures seemed to coincide with a recent "emergency" clean-up effort that another developer on the agile development team performed, and Remi was worried that this procedure was accidently deleted during the clean-up. He searched for a backup that was made before the clean-up project, and was able to restore the missing procedure. Its entire code was as follows.
CREATE PROCEDURE [dbo].[PUpd_Cal_ATM] AS -- +---------------------------------------------------------------------------+ -- | Description : Feeds the tables ATM | -- | Entry : none | -- | Output : Return (0 = OK) | -- +--------+------+-------+---------------------------------------------------+ -- | Date |Author|Version|Description | -- +--------+------+-------+---------------------------------------------------+ -- |06/12/07| XXX | 1.0 | Starting version | -- +--------+------+-------+---------------------------------------------------+ -- For now, this stored procedure is empty but it may be -- called by UpdateDatabase -- ========================================================================== -- return code -- ==========================================================================
It made little sense; this procedure had clearly not been feeding the database for three years, yet the data was only a week old. It took him three more full days to figure out the source: a hotfix to Microsoft Office caused a certain type of macro code in Excel to be considered insecure, and a certain type of macro was being used to feed the ATM Addressing system from Excel.
Oh, the joys of agile development.
https://thedailywtf.com/articles/classic-wtf-uncovering-nothing
|
Метки: CodeSOD |
Classic WTF: Emergency Faxes |
One of my first teenage jobs was to work as an office assistant. As this was the 90s, that meant I had to use and understand a fax machine. I… did not. The fax machine forever was a dark mystery to me, something that required strange incantations to work. As our summer break continues, at least I never caused this much trouble with the damn thing. Original --Remy
As far as technologies go, faxing is ancient. It predates the telephone by over a decade and, despite vast advances in scanning and email technology, the fax still remains a standard form of communication.
When a transmission goes out, the occasional telecommunication ‘hiccup’ or line noise can corrupt the fax. Most modern fax machines have some rudimentary error handling that will alerts the user that the fax should be resent.
This method of managing failures has been working so well that no one really saw fit to change it. That is, until a business analyst at Torre L’s company had a bright idea.
“What if the user isn’t babysitting the fax?" he worried. "What if the sender’s fax machine can’t detect that there was a problem? We should fax back an error report!”
Though the analyst's concern was valid, Torre and his group argued that sending back a failed fax transmission would not necessarily make it easier.
They argued that it could tie up the original sender's machine and could actually prevent someone from resending their original fax.
"It's okay," the analyst replied, "Our software can send and receive in parallel. This idea is the best thing since the iPod! It's bulletproof!"
But the arguments were for nothing; in management's eyes, the business analysts were always right and the feature was completed in short order. Named the "FaxBack" program, it did exactly as the name implied; it faxed a failed transmission back to the sender (as identified by Caller ID) moments after the failure occurred.
Everything went along without a hitch for a good long while. That is, until two police cruisers — sirens blazing — showed up at Torre’s office. They said they were responding to a 911 emergency call, but there was no emergency and nobody claimed to have dialed 911.
They officers left shortly thereafter, but, in the wee hours of the next morning, two different police cruisers rushed to the scene. Again, there was no emergency and no 911 call, so they too left without incident.
But the third time, when a cruiser showed up the following afternoon, the officers refused to leave until the source of the “disturbance” was identified. The police department was certain that a call came from a phone number within the company, and they demanded to know what exactly was going on.
After spending the rest of the afternoon and part of the evening of tracing phone calls internally, Torre traced the 911 calls to the data center, or more specifically, to FaxBack.
The faxes machines, like the office phones, had to dial "9" for an outside line. So when FaxBack responded to a failed fax from New Delhi by dialing "9", and then India's international country code of 11, a "special case" rule in the the telecom system kicked in.
Whoever had first installed the telecom system had the bright idea to treat “911” as a special case since, ordinarily, one might not think to first dial “9” in case of an emergency. The special case applied to the lines in the fax pool -- even if the caller would only ever be a computer.
https://thedailywtf.com/articles/classic-wtf-emergency-faxes
|
Метки: Feature Articles |
CodeSOD: Classic WTF: Logical Tiers? That Makes No Sense! |
It's that time of year when we take a short summer break, and that means we reach back into the archives for some classic WTFs that remind us of when things were better. Or worse. So much worse. Today, we find out where checkboxes come from. Original --Remy
Some developers just don't believe in "standards." I should know, I used to work with some of them. They had their own way of doing things, from reinventing the database to changing the web paradigm. I always found it ironic that these folks have a pretty good knowledge of the tools, but could never seem to figure out how to use 'em. Like Chris' predecessor, who seems to have done the equivalent of tightening screws with a voltammeter.
Ok, so I had to port over an ASP app to Coldfusion MX. It was a simple set of search pages so I didn't think it would take too long. Problem was, I couldn't find anywhere in the code where the HTML for one of the select boxes was. Silly me, I should have checked inside the SQL Server stored procedure first! And of course, this is just the tip of iceberg on this site. There were stored procedures that were used to build the actual HTML for the dynamic navigation as well.
CREATE PROCEDURE GetDepartmentDropDown AS IF ((SELECT COUNT(*) FROM Staff_DepartmentLink) > 0) BEGIN CREATE TABLE #HTML (src varchar(1000)) INSERT #HTML (src) Select ' INSERT #HTML (src) SELECT '' INSERT #HTML (src) SELECT DISTINCT '(varchar(6), DeptID) + '">' + DepartmentName + '' As src FROM StaffDepartments INNER JOIN Staff_DepartmentLink ON StaffDepartments.DepartmentID = Staff_DepartmentLink.DeptID ORDER BY src INSERT #HTML (src) SELECT '' SELECT src As 'rowsrc' FROM #HTML DROP TABLE #HTML END ELSE Select 'No Departments Defined' As rowsrc
https://thedailywtf.com/articles/classic-wtf-logical-tiers-that-makes-no-sense
|
Метки: CodeSOD |
Error'd: MathOverflow |
"I just logged in to StackOverlflow and gained two rep points! Wow, let's see who vot... uhmm... -20 +5 +10 +10 equals 2?" Ryan R. writes.
Brad W. wrote, "Whoever put this together at Wells Fargo was confused about verification."
"As it turns out, Microsoft's history is a bit longer than we expected," Ben writes.
Nate G. wrote, "Oh...wow...gee...thanks, Microsoft. I feel so valued."
"I'll say this. Fortunately, neither of the censored names is Cruella De Vil," Vivia writes.
Dustin wrote, "According to BJ's Wholesale Club, I'm the world's oldest living person!"
|
Метки: Error'd |