No, it's not bad practice to use a join operation in a DELETE statement. It's a common pattern.
I'm not sure whose told you that it was "bad programming", if they have any rationale for telling you that.
For delete rows from more than one table, there is a possibility that MySQL may perform the operations in an order which causes foreign key constraints to be violated (with InnoDB tables).
For example, MySQL may attempt to delete a row from head_doc before it deletes the related child rows from doc_details.
If you use a multiple-table DELETE statement involving InnoDB tables for which there are foreign key constraints, the MySQL optimizer might process tables in an order that differs from that of their parent/child relationship. In this case, the statement fails and rolls back. Instead, you should delete from a single table and rely on the ON DELETE capabilities that InnoDB provides to cause the other tables to be modified accordingly.
Reference: http://dev.mysql.com/doc/refman/5.5/en/delete.html
Or, we can sometimes workaround that issue by using separate statements to delete from each table.
We note that the query in the question will only only remove rows from head_doc that have a related child row in doc_details, because of the inner join operation. That will leave row(s) in head_doc if there aren't any child rows. That's not necessarily bad programming. But it is a bit odd.
Given that you want to remove rows from the two tables, you probably want an outer join, so you delete rows from head_doc even when no matching child rows exist in doc_detais
 DELETE d.* 
      , h.*
   FROM head_doc h
   LEFT
   JOIN doc_details d 
     ON d.id_doc = h.id_doc 
  WHERE h.id_doc =  ?
Given that you have id_doc in both tables, a simpler pattern would be to use two separate DELETE statements, first delete from the child table, then the parent:
 DELETE FROM doc_details WHERE id_doc = ?
 DELETE FROM head_doc    WHERE id_doc = ?
In cases where the rows to be removed from head_doc are identified by some other condition than on the id_doc column, on columns that are not available in doc_details, we can use a JOIN operation to identify the rows in the child table that should be removed
e.g. first, delete rows from the child table (using a JOIN operation)
 DELETE d.*
   FROM head_doc h
   JOIN doc_details d 
     ON d.id_doc = h.id_doc 
  WHERE h.somecol  < ?
    AND h.othercol = ?
Then, delete from the parent table:
 DELETE h.*
   FROM head_doc h
  WHERE h.somecol  < ?
    AND h.othercol = ?
In summary, using a JOIN operation in a DELETE is not necessarily "bad programming". There are some caveats we need to be aware of. Sometimes, using a join operation is the best approach. It really depends on what you want to achieve.